Como parar de escrever Stream.collect em Kotlin e começar a viver
Você já migrou de Java para Kotlin e se sentiu como um peixe fora d'água? Especialmente quando se tratava de streams. No Java 8, estávamos acostumados com cadeias de .stream().filter().map().collect(). E no Kotlin — puf! — não tem Stream.collect da forma habitual. Mas não entre em pânico. Na verdade, na biblioteca padrão do Kotlin existe tudo isso, só que mais prático, conciso e sem ruído desnecessário.
Neste artigo, vamos explorar quais análogos do Java 8 Stream.collect existem em Kotlin e mostrar isso com exemplos reais. Se você está procurando a resposta para a pergunta "What Java 8 Stream.collect equivalents are available in the standard Kotlin library?" — está no lugar certo. Vamos abordar as operações mais populares: desde a simples coleta em uma lista até agrupamentos complexos e junção de strings.
Pronto? Vamos nessa!
1. O caso mais simples: toList() e toSet()
Em Java, para coletar o resultado de um stream em uma lista, você escreve:
List<String> result = list.stream() .filter(s -> s.length() > 3) .collect(Collectors.toList());Em Kotlin, isso se transforma em uma única linha sem collect:
val result = list.filter { it.length > 3 }Sim, você ouviu direito. filter em Kotlin já retorna uma lista. Mas se você precisar especificar explicitamente o tipo da coleção, use toList(), toSet() ou toMutableList():
val numbers = listOf(1, 2, 3, 4, 5, 5)val uniqueNumbers: Set<Int> = numbers.toSet()// Resultado: [1, 2, 3, 4, 5]Conclusão: em Kotlin, a maioria das operações com coleções (filter, map, flatMap) já retorna uma lista. collect(Collectors.toList()) não é mais necessário.
2. Agrupamento: groupingBy em vez de Collectors.groupingBy
Agrupamento é uma das tarefas mais comuns. Em Java, fica assim:
Map<Integer, List<String>> byLength = list.stream() .collect(Collectors.groupingBy(String::length));Em Kotlin, existe a elegante função groupBy:
val words = listOf("gato", "código", "gato", "gatinho")val byLength = words.groupBy { it.length }// Resultado: {4=[gato], 6=[código], 7=[gatinho]}Mas há um detalhe: groupBy retorna diretamente um Map<K, List<V>>. E se você precisar de algo mais complexo, como contar o número de elementos em cada grupo? Em Java, você usaria Collectors.counting():
Map<Integer, Long> countByLength = list.stream() .collect(Collectors.groupingBy(String::length, Collectors.counting()));Em Kotlin, para isso existe groupingBy + eachCount:
val countByLength = words.groupingBy { it.length }.eachCount()// Resultado: {4=1, 6=1, 7=1}E se você precisar coletar não uma lista, mas, por exemplo, um conjunto? Use groupBy com transformação:
val byLengthSet = words.groupBy(keySelector = { it.length }, valueTransform = { it.uppercase() })// Resultado: {4=[GATO], 6=[CÓDIGO], 7=[GATINHO]}Dica: se você precisar de algo entre groupBy e groupingBy, use groupingBy — ele é lazy e permite aplicar diferentes agregações (eachCount, fold, reduce).
3. Junção de strings: joinToString() em vez de Collectors.joining()
Em Java, a junção de strings com um separador:
String joined = list.stream() .collect(Collectors.joining(", "));Em Kotlin, isso é ainda mais simples:
val words = listOf("Kotlin", "Java", "Python")val joined = words.joinToString(",