En el pasado, he dicho que copiar una colección de manera segura haga algo como:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
o
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Pero, ¿son estos constructores de "copia", métodos y flujos de creación estáticos similares, realmente seguros y dónde se especifican las reglas? Por seguro, quiero decir, son las garantías básicas de integridad semántica ofrecidas por el lenguaje Java y las colecciones aplicadas contra un llamador malicioso, suponiendo SecurityManager
que esté respaldado por un razonable y que no haya fallas.
Estoy contento con el lanzamiento método ConcurrentModificationException
, NullPointerException
, IllegalArgumentException
, ClassCastException
, etc, o tal vez incluso colgado.
He elegido String
como ejemplo un argumento de tipo inmutable. Para esta pregunta, no me interesan las copias profundas para colecciones de tipos mutables que tienen sus propios trucos.
(Para ser claros, he mirado el código fuente de OpenJDK y tengo algún tipo de respuesta para ArrayList
y TreeSet
).
NavigableSet
y otras Comparable
colecciones basadas a veces pueden detectar si una clase no se implementa compareTo()
correctamente y lanzar una excepción. No está claro qué quiere decir con argumentos no confiables. ¿Te refieres a un malhechor que crea una colección de cadenas malas y cuando las copias a tu colección, algo malo sucede? No, el marco de colecciones es bastante sólido, existe desde 1.2.
HashSet
(y todas las demás colecciones de hash en general) se basa en la corrección / integridad de la hashCode
implementación de los elementos, TreeSet
y PriorityQueue
depende de Comparator
(y ni siquiera puede crea una copia equivalente sin aceptar el comparador personalizado si lo hay), EnumSet
confía en la integridad del enum
tipo particular que nunca se verifica después de la compilación, por lo que un archivo de clase, no generado javac
o hecho a mano, puede subvertirlo.
new TreeSet<>(strs)
dónde strs
está a NavigableSet
. Esta no es una copia masiva, ya que el resultado TreeSet
utilizará el comparador de la fuente, que incluso es necesario para mantener la semántica. Si está bien con solo procesar los elementos contenidos, este toArray()
es el camino a seguir; incluso mantendrá el orden de iteración. Cuando está bien con "tomar elemento, validar elemento, usar elemento", ni siquiera necesita hacer una copia. Los problemas comienzan cuando desea verificar todos los elementos, seguido de la utilización de todos los elementos. Entonces, no puede confiar en una TreeSet
copia con un comparador personalizado
checkcast
para cada elemento, es toArray
con un tipo específico. Siempre terminamos en eso. Las colecciones genéricas ni siquiera conocen su tipo de elemento real, por lo que sus constructores de copias no pueden proporcionar una funcionalidad similar. Por supuesto, puede diferir cualquier verificación al uso previo correcto, pero entonces, no sé a qué apuntan sus preguntas. No necesita "integridad semántica", cuando está de acuerdo con verificar y fallar inmediatamente antes de usar elementos.