En Java 8, existe el Stream.collect
que permite agregaciones en colecciones. En Kotlin, esto no existe de la misma manera, aparte de quizás como una colección de funciones de extensión en stdlib. Pero no está claro cuáles son las equivalencias para diferentes casos de uso.
Por ejemplo, en la parte superior de JavaDocCollectors
hay ejemplos escritos para Java 8, y cuando los transfiere a Kolin no puede usar las clases de Java 8 cuando está en una versión JDK diferente, por lo que es probable que se escriban de manera diferente.
En términos de recursos en línea que muestran ejemplos de colecciones de Kotlin, generalmente son triviales y realmente no se comparan con los mismos casos de uso. ¿Cuáles son buenos ejemplos que realmente coinciden con los casos documentados para Java 8 Stream.collect
? La lista hay:
- Acumula nombres en una lista
- Acumula nombres en un TreeSet
- Convierta elementos en cadenas y concatenelos, separados por comas
- Calcular la suma de los salarios del empleado
- Agrupar empleados por departamento
- Calcular la suma de salarios por departamento
- Particionar a los estudiantes para aprobar y reprobar
Con detalles en el JavaDoc vinculado anteriormente.
Nota: esta pregunta está escrita y respondida intencionalmente por el autor ( Preguntas con respuesta propia ), de modo que las respuestas idiomáticas a los temas de Kotlin más frecuentes están presentes en SO. También para aclarar algunas respuestas realmente antiguas escritas para alfas de Kotlin que no son precisas para el Kotlin actual.
fuente
collect(Collectors.toList())
o similar, puede encontrar este problema: stackoverflow.com/a/35722167/3679676 (el problema, con soluciones)Respuestas:
Hay funciones en Kotlin stdlib para promediar, contar, distinguir, filtrar, buscar, agrupar, unir, mapear, min, max, particionamiento, segmentación, clasificación, suma, a / desde matrices, a / desde listas, a / desde mapas , unión, co-iteración, todos los paradigmas funcionales y más. Entonces puede usarlos para crear pequeños 1-liners y no hay necesidad de usar la sintaxis más complicada de Java 8.
Creo que lo único que falta en laCollectors
clase incorporada de Java 8 es el resumen (pero en otra respuesta a esta pregunta es una solución simple) .Una cosa que falta en ambos es la agrupación por conteo, que se ve en otra respuesta de desbordamiento de pila y también tiene una respuesta simple. Otro caso interesante es este también de Stack Overflow: forma idiomática de secuenciar derramada en tres listas usando Kotlin . Y si desea crear algo comoStream.collect
para otro propósito, vea Custom Stream.collect en KotlinEDITAR 11.08.2017: Se agregaron operaciones de recolección fragmentadas / en ventanas en kotlin 1.2 M2, consulte https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/
Siempre es bueno explorar la Referencia API para kotlin.collections en su conjunto antes de crear nuevas funciones que ya puedan existir allí.
Aquí hay algunas conversiones de
Stream.collect
ejemplos de Java 8 al equivalente en Kotlin:Acumula nombres en una lista
Convierta elementos en cadenas y concatenelos, separados por comas
Calcular la suma de los salarios del empleado
Agrupar empleados por departamento
Calcular la suma de salarios por departamento
Particionar a los estudiantes para aprobar y reprobar
Nombres de miembros masculinos
Agrupe los nombres de los miembros en la lista por género
Filtrar una lista a otra lista
Encontrar la cadena más corta de una lista
Contar elementos en una lista después de aplicar el filtro
y así sucesivamente ... En todos los casos, no se requiere doblar, reducir u otra funcionalidad especial para imitar
Stream.collect
. Si tiene más casos de uso, agréguelos en los comentarios y ¡podemos ver!Sobre la pereza
Si desea procesar de forma diferida una cadena, puede convertirla en un
Sequence
usoasSequence()
antes de la cadena. Al final de la cadena de funciones, generalmente terminas con unSequence
también. A continuación, puede utilizartoList()
,toSet()
,toMap()
o alguna otra función para materializar laSequence
al final.¿Por qué no hay tipos?
Notará que los ejemplos de Kotlin no especifican los tipos. Esto se debe a que Kotlin tiene inferencia de tipo completa y es completamente segura en tiempo de compilación. Más que Java porque también tiene tipos anulables y puede ayudar a prevenir el temido NPE. Entonces esto en Kotlin:
es lo mismo que:
Debido a que Kotlin sabe lo que
people
es, y esapeople.age
es,Int
por lo tanto, la expresión de filtro solo permite la comparación con anInt
, y esepeople.name
es un paso,String
por lo tanto, elmap
paso produce unList<String>
(solo lecturaList
deString
).Ahora, si
people
fuera posiblenull
, como en unList<People>?
entonces:Devuelve un
List<String>?
que necesitaría ser anulado ( o usar uno de los otros operadores de Kotlin para valores anulables, vea esta forma idiomática de Kotlin para tratar con valores anulables y también una forma idiomática de manejar la lista nula o vacía en Kotlin )Ver también:
fuente
Para ejemplos adicionales, aquí están todos los ejemplos de Java 8 Stream Tutorial convertidos a Kotlin. El título de cada ejemplo se deriva del artículo fuente:
Cómo funcionan las transmisiones
Diferentes tipos de corrientes # 1
o cree una función de extensión en String llamada ifPresent:
Ver también:
apply()
funciónVer también: Funciones de extensión
Ver también:
?.
Operador Safe Call , y en general anulabilidad: en Kotlin, ¿cuál es la forma idiomática de tratar con valores anulables, haciendo referencia o convirtiéndolos?Diferentes tipos de corrientes # 2
Diferentes tipos de corrientes # 3
Diferentes tipos de corrientes # 4
Diferentes tipos de corrientes # 5
Diferentes tipos de corrientes # 6
Diferentes tipos de corrientes # 7
Por qué es importante el pedido
Esta sección de Java 8 Stream Tutorial es la misma para Kotlin y Java.
Reutilizando Streams
En Kotlin, depende del tipo de colección si se puede consumir más de una vez. A
Sequence
genera un nuevo iterador cada vez y, a menos que afirme "usar solo una vez", puede restablecer el inicio cada vez que se actúa sobre él. Por lo tanto, aunque lo siguiente falla en la secuencia Java 8, pero funciona en Kotlin:Y en Java para obtener el mismo comportamiento:
Por lo tanto, en Kotlin, el proveedor de los datos decide si se puede restablecer y proporcionar un nuevo iterador o no. Pero si desea restringir intencionalmente una
Sequence
iteración de una vez, puede usar laconstrainOnce()
función de laSequence
siguiente manera:Operaciones avanzadas
Recopile el ejemplo # 5 (sí, omití los que ya están en la otra respuesta)
Y como nota al margen, en Kotlin podemos crear clases de datos simples e instanciar los datos de prueba de la siguiente manera:
Recoge el ejemplo # 6
Ok, un caso más interesante aquí para Kotlin. Primero, las respuestas incorrectas para explorar las variaciones de la creación
Map
de una colección / secuencia:Y ahora para la respuesta correcta:
Solo necesitábamos unir los valores coincidentes para colapsar las listas y proporcionar un transformador
jointToString
para pasar de unaPerson
instancia a otraPerson.name
.Recoge el ejemplo # 7
Ok, esto se puede hacer fácilmente sin una costumbre
Collector
, así que vamos a resolverlo a la manera de Kotlin, luego inventemos un nuevo ejemplo que muestre cómo hacer un proceso similar para elCollector.summarizingInt
que no existe de forma nativa en Kotlin.¡No es mi culpa que hayan elegido un ejemplo trivial! Ok, aquí hay un nuevo
summarizingInt
método para Kotlin y una muestra coincidente:Resumen de ejemplo
Pero es mejor crear una función de extensión, 2 para que coincida con los estilos en Kotlin stdlib:
Ahora tiene dos formas de usar las nuevas
summarizingInt
funciones:Y todo esto produce los mismos resultados. También podemos crear esta extensión para trabajar
Sequence
y para los tipos primitivos apropiados.Por diversión, compare el código JDK de Java con el código personalizado de Kotlin requerido para implementar este resumen.
fuente
.map { it.substring(1).toInt() }
: como bien sabe, el tipo inferido es uno de poder kotlin.Hay algunos casos en los que es difícil evitar llamar
collect(Collectors.toList())
o algo similar. En esos casos, puede cambiar más rápidamente a un equivalente de Kotlin utilizando funciones de extensión como:Entonces puedes simplemente
stream.toList()
ostream.asSequence()
volver a la API de Kotlin. Un caso como el queFiles.list(path)
te obliga a entrarStream
cuando no lo deseas, y estas extensiones pueden ayudarte a volver a las colecciones estándar y la API de Kotlin.fuente
Más sobre la pereza
Tomemos la solución de ejemplo para "Calcular suma de salarios por departamento" dada por Jayson:
Para hacer esto perezoso (es decir, evitar crear un mapa intermedio en el
groupBy
paso), no es posible usarloasSequence()
. En cambio, debemos usargroupingBy
yfold
operar:Para algunas personas, esto puede incluso ser más legible, ya que no se trata de entradas de mapa: el
it.value
parte de la solución también me resultó confusa al principio.Dado que este es un caso común y preferiríamos no escribir
fold
cada vez, puede ser mejor simplemente proporcionar unasumBy
función genérica enGrouping
:para que podamos simplemente escribir:
fuente