tejer
JDK 16 ahora incluye un toList()
método directamente en Stream
instancias. En versiones anteriores de Java, siempre tenía que usar el collect
método y proporcionar un Collector
instancia.
El nuevo método obviamente tiene menos caracteres para escribir. ¿Ambos métodos son intercambiables o hay diferencias sutiles que uno debe tener en cuenta?
var newList = someCollection.stream()
.map(x -> mapX(x))
.filter(x -> filterX(x))
.toList();
// vs.
var oldList = someCollection.stream()
.map(x -> mapX(x))
.filter(x -> filterX(x))
.collect(Collectors.toList());
(Esta pregunta es similar a ¿Funcionaría mejor Stream.toList() que Collectors.toList(), pero se centra en el comportamiento y no (solo) en el rendimiento).
Arvind Kumar Avinash
Una diferencia es que Stream.toList()
provee un List
implementación que es inmutable (tipo ImmutableCollections.ListN
que no se pueden agregar o clasificar) similar a la proporcionada por List.of()
y en contraste con el mutable (se puede cambiar y ordenar) ArrayList
proporcionado por Stream.collect(Collectors.toList())
.
Manifestación:
import java.util.stream.Stream;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = Stream.of("Hello").toList();
System.out.println(list);
list.add("Hi");
}
}
Producción:
[Hello]
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
at Main.main(Main.java:8)
por favor, compruebe Este artículo para más detalles.
Actualizar:
Curiosamente, Stream.toList()
devuelve un null
lista que contiene s con éxito.
import java.util.stream.Stream;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Object> list = Stream.of(null, null).toList();
System.out.println(list);
}
}
Producción:
[null, null]
Por otro lado, List.of(null, null)
lanza NullPointerException
.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Object> list = List.of(null, null);
}
}
Producción:
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:208)
at java.base/java.util.ImmutableCollections$List12.<init>(ImmutableCollections.java:453)
at java.base/java.util.List.of(List.java:827)
at Main.main(Main.java:5)
Nota: he usado openjdk-16-ea+34_osx-x64 para compilar y ejecutar el código Java SE 16.
Recursos útiles:
- Error de JDK #JDK-8180352
- ¿Llamar al método varargs de Java con un solo argumento nulo?
-
IIRC,
Collectors.toList()
tampoco se garantiza que nos proporcione una lista mutable. Simplemente sucede que lo hace en las versiones de Java que hemos visto hasta ahora.– Olé VV
30 de enero de 2021 a las 18:14
-
@OleV.V. – Correcto. El artículo vinculado en mi respuesta lo menciona como:
Although there are no guarantees regarding the “type, mutability, serializability, or thread-safety” on the List provided by Collectors.toList(), it is expected that some may have realized it’s currently an ArrayList and have used it in ways that depend on the characteristics of an ArrayList.
– Arvind Kumar Avinash
30 de enero de 2021 a las 18:26
-
@Caramiriel La
List
La interfaz se diseñó a propósito con una serie de métodos opcionales, es decir, métodos que las clases de implementación no necesitan implementar y que muchas implementaciones no implementan. No eres el primero en preguntarse.– Olé VV
4 de febrero de 2021 a las 20:44
-
No es realmente una afirmación correcta decir que
collect(toList())
devuelve una lista mutable; la especificación es muy clara que hace sin garantías en cuanto a la mutabilidad de la lista devuelta. La implementación actual pasa a devolver unArrayList
ahora mismo, pero la especificación se escribió explícitamente para permitir que eso cambie. Si quieres una lista mutable, usatoCollection(ArrayList::new)
.– Brian Goetz
19 de febrero de 2021 a las 17:07
-
Es desafortunado que
List
no se dividió enReadableList
conget()
etc yWritableList
conset()
,add()
etc., pero es demasiado tarde para arreglarlo ahora. (nota: originalmente vine aquí para sugerirtoCollection(ArrayList::new)
, que es lo que siempre uso cuando necesito una lista mutable, pero el hombre mismo se me adelantó. 😂)– Clemente Cherlin
22 de febrero de 2021 a las 14:05
ZhekaKozlov
Aquí hay una pequeña tabla que resume las diferencias entre Stream.collect(Collectors.toList())
, Stream.collect(Collectors.toUnmodifiableList())
y Stream.toList()
:
Método | Garantiza la inmodificabilidad | Permite nulos |
---|---|---|
collect(toList()) |
No | Sí |
collect(toUnmodifiableList()) |
Sí | No |
toList() |
Sí | Sí |
Otra pequeña diferencia:
// Compiles
List<CharSequence> list = Stream.of("hello", "world").collect(toList());
// Error
List<CharSequence> list = Stream.of("hello", "world").toList();
-
El nuevo método Stream.toList() no produce una lista no modificable ni un atajo para recopilar (toUnmodifiableList()), porque toUnmodifiableList() no acepta valores nulos. La implementación de Stream.toList() no está restringida por la interfaz Collector; por lo tanto, Stream.toList() asigna menos memoria. Eso lo hace óptimo para usar cuando el tamaño de la transmisión se conoce de antemano. blogs.oracle.com/javamagazine/hidden-gems-jdk16-jdk17-jep ¿Puedes validar las declaraciones anteriores?
– deepakl.2000
28 de julio de 2021 a las 18:19
-
Agregaría que el primero se introdujo en Java 8, el segundo en Java 10 y el tercero en Java 16.
–Guillaume Husta
5 de diciembre de 2022 a las 10:05
.collect(toList())
y toList()
comportarse diferente con respecto a la compatibilidad de subtipo de los elementos en las listas creadas.
Echa un vistazo a las siguientes alternativas:
List<Number> old = Stream.of(0).collect(Collectors.toList());
funciona bien, aunque recopilamos un flujo de Entero en una lista de Número.List<Number> new = Stream.of(0).toList();
es la versión equivalente de Java 16+, pero no compila (cannot convert from List<Integer> to List<Number>
; al menos en ecj, el compilador Java de Eclipse).
Hay al menos 2 soluciones para corregir el error de compilación:
- Emitir explícitamente al tipo buscado
List<Number> fix1 = Stream.of(0).map(Number.class::cast).toList();
- permitir subtipos en la colección de resultados:
List<? extends Number> fix2 = Stream.of(0).toList();
Según tengo entendido, la causa raíz es la siguiente: el tipo genérico T de Java 16 toList()
es el mismo que el tipo genérico T del propio Stream. Sin embargo, el tipo genérico T de Collectors.toList() se propaga desde el lado izquierdo de la asignación. Si esos 2 tipos son diferentes, es posible que vea errores al reemplazar todas las llamadas anteriores.