Java 9 introdujo nuevos métodos de fábrica para listas, List.of
:
List<String> strings = List.of("first", "second");
¿Cuál es la diferencia entre la opción anterior y la nueva? Es decir, cuál es la diferencia entre esto:
Arrays.asList(1, 2, 3);
y esto:
List.of(1, 2, 3);
Arrays.asList
devuelve una lista mutable mientras que la lista devuelta por List.of
es inmutable:
List<Integer> list = Arrays.asList(1, 2, null);
list.set(1, 10); // OK
List<Integer> list = List.of(1, 2, 3);
list.set(1, 10); // Fails with UnsupportedOperationException
Arrays.asList
permite elementos nulos mientras List.of
no:
List<Integer> list = Arrays.asList(1, 2, null); // OK
List<Integer> list = List.of(1, 2, null); // Fails with NullPointerException
contains
se comporta de manera diferente con valores nulos:
List<Integer> list = Arrays.asList(1, 2, 3);
list.contains(null); // Returns false
List<Integer> list = List.of(1, 2, 3);
list.contains(null); // Fails with NullPointerException
Arrays.asList
devuelve una vista de la matriz pasada, por lo que los cambios en la matriz también se reflejarán en la lista. Para List.of
esto no es verdad:
Integer[] array = {1,2,3};
List<Integer> list = Arrays.asList(array);
array[1] = 10;
System.out.println(list); // Prints [1, 10, 3]
Integer[] array = {1,2,3};
List<Integer> list = List.of(array);
array[1] = 10;
System.out.println(list); // Prints [1, 2, 3]
Ver el JavaDocs y esto hablar por Stuart Marks (o versiones anteriores del mismo).
Usaré lo siguiente para los ejemplos de código:
List<Integer> listOf = List.of(...);
List<Integer> asList = Arrays.asList(...);
List<Integer> unmodif = Collections.unmodifiableList(asList);
Inmutabilidad estructural (O: inmodificabilidad)
Cualquier intento de estructuralmente cambio List.of
resultará en un UnsupportedOperationException
. Eso incluye operaciones tales como agregar, establecer y retirar. Sin embargo, puede cambiar el contenido de los objetos en la lista (si los objetos no son inmutables), por lo que la lista no es “completamente inmutable”.
Este es el mismo destino para las listas no modificables creadas con Collections.unmodifiableList
. Sólo esta lista es una vista de la lista original, por lo que puede cambiar si cambia la lista original.
Arrays.asList
no es completamente inmutable, no tiene una restricción en set
.
listOf.set(1, "a"); // UnsupportedOperationException
unmodif.set(1, "a"); // UnsupportedOperationException
asList.set(1, "a"); // modified unmodif! unmodif is not truly unmodifiable
Del mismo modo, cambiar la matriz de respaldo (si la mantiene) cambiará la lista.
La inmutabilidad estructural viene con muchos efectos secundarios relacionados con la codificación defensiva, la concurrencia y la seguridad que están más allá del alcance de esta respuesta.
Hostilidad nula
List.of
y cualquier colección desde Java 1.5 no permite null
como elemento. intentando pasar null
como un elemento o incluso una búsqueda resultará en un NullPointerException
.
Ya que Arrays.asList
es una colección de 1.2 (The Collections Framework), permite null
s.
listOf.contains(null); // NullPointerException
unmodif.contains(null); // allowed
asList.contains(null); // allowed
formulario serializado
Ya que List.of
se introdujo en Java 9 y las listas creadas por este método tienen su propia forma serializada (binaria), no se pueden deserializar en versiones anteriores de JDK (no compatibilidad binaria). Sin embargo, puede deserializar/deserializar con JSON, por ejemplo.
Identidad
Arrays.asList
llamadas internas new ArrayList
que garantiza la desigualdad de referencia.
List.of
depende de la implementación interna. Las instancias devueltas pueden tener igualdad de referencia, pero dado que esto no está garantizado, no puede confiar en ello.
asList1 == asList2; // false
listOf1 == listOf2; // true or false
Vale la pena mencionar que las listas son iguales (a través de List.equals
) si contienen los mismos elementos en el mismo orden, independientemente de cómo se crearon o qué operaciones admiten.
asList.equals(listOf); // true i.f.f. same elements in same order
Implementación (advertencia: los detalles pueden cambiar con las versiones)
Si el número de elementos en la lista de List.of
es 2 o menos, los elementos se almacenan en campos de una clase especializada (interna). Un ejemplo es la lista que almacena 2 elementos (fuente parcial):
static final class List2<E> extends AbstractImmutableList<E> {
private final E e0;
private final E e1;
List2(E e0, E e1) {
this.e0 = Objects.requireNonNull(e0);
this.e1 = Objects.requireNonNull(e1);
}
}
De lo contrario, se almacenan en una matriz de manera similar a Arrays.asList
.
Eficiencia de tiempo y espacio
los List.of
las implementaciones basadas en campos (tamaño <2) funcionan un poco más rápido en algunas operaciones. Como ejemplos, size()
puede devolver una constante sin obtener la longitud de la matriz, y contains(E e)
no requiere sobrecarga de iteración.
Construyendo una lista no modificable a través de List.of
también es más rápido. Compare el constructor anterior con 2 asignaciones de referencia (e incluso la de una cantidad arbitraria de elementos) para
Collections.unmodifiableList(Arrays.asList(...));
que crea 2 listas más otros gastos generales. En términos de espacio, te ahorras el UnmodifiableList
envoltorio más algunos centavos. En última instancia, los ahorros en el HashSet
equivalentes son más convincentes.
Tiempo de conclusión: uso List.of
cuando quieres una lista que no cambia y Arrays.asList
cuando desee una lista que pueda cambiar (como se muestra arriba).
Resumamos las diferencias entre Lista de y Arreglos.asList
-
List.of
se puede usar mejor cuando el conjunto de datos es menor y no cambia, mientras que Arrays.asList
se puede usar mejor en el caso de un conjunto de datos grande y dinámico.
-
List.of
ocupa mucho menos espacio de sobrecarga porque tiene una implementación basada en campo y consume menos espacio de almacenamiento dinámico, tanto en términos de sobrecarga fija como por elemento. tiempo Arrays.asList
ocupa más espacio de sobrecarga porque durante la inicialización crea más objetos en el montón.
-
Colección devuelta por List.of
es inmutable y, por lo tanto, seguro para subprocesos, mientras que Collection devuelto por Arrays.asList
es mutable y no es seguro para subprocesos. (Las instancias de colección inmutable generalmente consumen mucha menos memoria que sus contrapartes mutables).
-
List.of
no permite nulo elementos mientras Arrays.asList
permite nulo elementos.
Además de las respuestas anteriores, hay ciertas operaciones en las que ambos List::of
y Arrays::asList
diferir de:
+----------------------+---------------+----------+----------------+---------------------+
| Operations | SINGLETONLIST | LIST::OF | ARRAYS::ASLIST | JAVA.UTIL.ARRAYLIST |
+----------------------+---------------+----------+----------------+---------------------+
| add | ❌ | ❌ | ❌ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| addAll | ❌ | ❌ | ❌ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| clear | ❌ | ❌ | ❌ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| remove | ❌ | ❌ | ❌ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| removeAll | ❗️ | ❌ | ❗️ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| retainAll | ❗️ | ❌ | ❗️ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| replaceAll | ❌ | ❌ | ✔️ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| set | ❌ | ❌ | ✔️ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| sort | ✔️ | ❌ | ✔️ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| remove on iterator | ❌ | ❌ | ❌ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
| set on list-iterator | ❌ | ❌ | ✔️ | ✔️ |
+----------------------+---------------+----------+----------------+---------------------+
- ✔️ significa que el método es compatible
- ❌ significa que llamar a este método arrojará una UnsupportedOperationException
- ❗️ significa que el método es compatible solo si los argumentos del método no causan una mutación, por ejemplo, Collections.singletonList(“foo”).retainAll(“foo”) está bien, pero Collections.singletonList(“foo”).retainAll(“bar” ) lanza una UnsupportedOperationException
Más sobre Colecciones::singletonList vs. Lista de
Arrays.asList(1, 2, 3);
A de tamaño fijo Lista se creará:
public static void main(String[] args) {
List<Integer> asList = Arrays.asList(1, 2, 3, 4, 5);
asList.add(6); // java.lang.UnsupportedOperationException
asList.remove(0); // java.lang.UnsupportedOperationException
asList.set(0, 0); // allowed
}
Lista.de(1, 2, 3);
A Inmutable (Java 9)/No modificable (Java 11) Lista se creará:
public static void main(String[] args) {
List<Integer> listOf = List.of(1, 2, 3, 4, 5);
listOf.add(6); // java.lang.UnsupportedOperationException
listOf.remove(0); // java.lang.UnsupportedOperationException
listOf.set(0, 0); // java.lang.UnsupportedOperationException
}
Ver también esto hablar por Stuart “Beaker” Marks.
– usuario1803551
5 oct 2017 a las 11:40
@ user1803551 Aunque entiendo su frustración, este razonamiento podría sentar un precedente realmente no deseado. Mucho de las preguntas aquí tienen una respuesta que está ‘claramente establecida’ (dependiendo de cómo se defina esto). Le insto a que lleve esta discusión a meta, pero estoy bastante seguro de que tal discusión ya debería existir (y espero que alguien pueda encontrarla y vincularla 🙂
– Dimitris Fasarakis Hilliard
5 de octubre de 2017 a las 13:23
@ user1803551 Javadocs no menciona la diferencia entre los detalles de implementación de estos dos métodos (como el consumo de espacio o el rendimiento). Creo que a la gente también le gustaría saber estos detalles.
– Zheka Kozlov
5 oct 2017 a las 13:33
@ZhekaKozlov La respuesta aceptada y súper votada tampoco. ¿Qué te dice eso acerca de los estándares aceptados? incluso tiene menos información que en los documentos (serialización, identidad, pedido). En todo caso, presente una solicitud a OpenJDK para agregar esa información.
– usuario1803551
6 de octubre de 2017 a las 3:20
Esta pregunta se está discutiendo en meta.
– Dimitris Fasarakis Hilliard
6 de octubre de 2017 a las 7:15