No Java 8, existe o Stream.collect
que permite agregações em coleções. No Kotlin, isso não existe da mesma maneira, exceto talvez como uma coleção de funções de extensão no stdlib. Mas não está claro quais são as equivalências para diferentes casos de uso.
Por exemplo, na parte superior do JavaDoc,Collectors
há exemplos escritos para Java 8 e, ao portá-los no Kolin, você não pode usar as classes Java 8 quando estiver em uma versão JDK diferente, portanto, provavelmente elas devem ser escritas de maneira diferente.
Em termos de recursos online que mostram exemplos de coleções do Kotlin, eles geralmente são triviais e não se comparam realmente aos mesmos casos de uso. Quais são os bons exemplos que realmente correspondem aos casos documentados para Java 8 Stream.collect
? A lista existe:
- Acumular nomes em uma lista
- Acumular nomes em um TreeSet
- Converter elementos em seqüências de caracteres e concatená-los, separados por vírgulas
- Calcular soma dos salários do empregado
- Agrupar funcionários por departamento
- Calcular a soma dos salários por departamento
- Divida os alunos em aprovados e reprovados
Com detalhes no JavaDoc vinculado acima.
Nota: esta pergunta é intencionalmente escrita e respondida pelo autor ( Perguntas respondidas automaticamente ), para que as respostas idiomáticas aos tópicos mais comuns do Kotlin estejam presentes no SO. Também para esclarecer algumas respostas realmente antigas escritas para alfas do Kotlin que não são precisas para o Kotlin atual.
fonte
collect(Collectors.toList())
ou similar, você pode bater esse problema: stackoverflow.com/a/35722167/3679676 (o problema, com soluções alternativas)Respostas:
Existem funções no Kotlin stdlib para média, contagem, distinto, filtragem, localização, agrupamento, junção, mapeamento, min, max, particionamento, fatia, classificação, soma, de / para matrizes, de / para listas, de / para mapas , união, co-iteração, todos os paradigmas funcionais e muito mais. Portanto, você pode usá-los para criar pequenos liners 1 e não há necessidade de usar a sintaxe mais complicada do Java 8.
Acho que a única coisa que falta naCollectors
classe Java 8 interna é a sumarização (mas em outra resposta a essa pergunta é uma solução simples) .Uma coisa que falta nos dois é o lote por contagem, que é visto em outra resposta do Stack Overflow e tem uma resposta simples também. Outro caso interessante é este também da Stack Overflow: maneira linguística de espalhar a sequência em três listas usando o Kotlin . E se você deseja criar algo parecidoStream.collect
com outro objetivo, consulte Custom Stream.collect no KotlinEDIT 11.08.2017: As operações de coleta em partes / janelas foram adicionadas no kotlin 1.2 M2, consulte https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/
É sempre bom explorar a Referência da API para kotlin.collections como um todo antes de criar novas funções que já possam existir lá.
Aqui estão algumas conversões de
Stream.collect
exemplos do Java 8 para o equivalente no Kotlin:Acumular nomes em uma lista
Converter elementos em seqüências de caracteres e concatená-los, separados por vírgulas
Calcular soma dos salários do empregado
Agrupar funcionários por departamento
Calcular a soma dos salários por departamento
Divida os alunos em aprovados e reprovados
Nomes dos membros masculinos
Agrupe nomes de membros da lista por gênero
Filtrar uma lista para outra lista
Localizando uma string mais curta
Contando itens em uma lista após a aplicação do filtro
e continua ... Em todos os casos, nenhuma dobra especial, redução ou outra funcionalidade foi necessária para imitar
Stream.collect
. Se você tiver outros casos de uso, adicione-os nos comentários e podemos ver!Sobre preguiça
Se você deseja processar preguiçosamente uma cadeia, pode converter para uma
Sequence
utilizaçãoasSequence()
antes da cadeia. No final da cadeia de funções, você geralmente acaba com umSequence
também. Então você pode usartoList()
,toSet()
,toMap()
ou alguma outra função para materializar oSequence
no final.Por que não existem tipos?!?
Você notará que os exemplos do Kotlin não especificam os tipos. Isso ocorre porque o Kotlin tem inferência de tipo completa e é completamente seguro em tempo de compilação. Mais do que Java, porque também possui tipos anuláveis e pode ajudar a evitar o temível NPE. Então, isso em Kotlin:
é o mesmo que:
Porque Kotlin sabe o que
people
é, e quepeople.age
éInt
, portanto, a expressão de filtro só permite comparação com umaInt
, e quepeople.name
é umString
, por conseguinte, omap
passo produz umList<String>
(somente leituraList
deString
).Agora, se
people
possívelnull
, como em umList<People>?
então:Retorna um
List<String>?
que precisaria ser verificado como nulo ( ou use um dos outros operadores do Kotlin para obter valores anuláveis; consulte esta maneira idiomática do Kotlin para lidar com valores anuláveis e também a maneira linguística de lidar com lista anulável ou vazia no Kotlin )Veja também:
fonte
Para exemplos adicionais, aqui estão todas as amostras do Java 8 Stream Tutorial convertidas para Kotlin. O título de cada exemplo é derivado do artigo de origem:
Como os fluxos funcionam
Diferentes tipos de fluxos # 1
ou crie uma função de extensão na String chamada ifPresent:
Veja também:
apply()
functionVeja também: Funções de extensão
Consulte também:
?.
Operador de chamada segura e, em geral, anulabilidade: no Kotlin, qual é a maneira idiomática de lidar com valores anuláveis, referenciando-os ou convertendo-osDiferentes tipos de fluxos # 2
Diferentes tipos de fluxos # 3
Diferentes tipos de fluxos # 4
Diferentes tipos de fluxos # 5
Diferentes tipos de fluxos # 6
Diferentes tipos de fluxos # 7
Por que a ordem é importante
Esta seção do Tutorial do Java 8 Stream é a mesma para o Kotlin e o Java.
Reutilizando fluxos
No Kotlin, depende do tipo de coleção se ela pode ser consumida mais de uma vez. A
Sequence
gera um novo iterador toda vez e, a menos que afirme "usar apenas uma vez", ele pode redefinir o início sempre que é acionado. Portanto, enquanto o seguinte falha no fluxo do Java 8, mas funciona no Kotlin:E em Java, para obter o mesmo comportamento:
Portanto, no Kotlin, o provedor dos dados decide se ele pode reiniciar e fornecer um novo iterador ou não. Mas se você deseja restringir intencionalmente uma
Sequence
iteração de uma vez, é possível usar aconstrainOnce()
função daSequence
seguinte maneira:Operações avançadas
Colete o exemplo 5 (sim, eu pulei aqueles que já estão na outra resposta)
E como uma observação lateral, no Kotlin, podemos criar classes de dados simples e instanciar os dados de teste da seguinte maneira:
Colete o exemplo # 6
Ok, um caso mais interessante aqui para Kotlin. Primeiro, as respostas erradas para explorar as variações da criação de a
Map
partir de uma coleção / sequência:E agora a resposta correta:
Nós apenas precisávamos juntar os valores correspondentes para recolher as listas e fornecer um transformador
jointToString
para passar daPerson
instância para oPerson.name
.Colete o exemplo # 7
Ok, este pode ser feito facilmente sem um costume
Collector
, então vamos resolvê-lo da maneira Kotlin e então inventar um novo exemplo que mostra como executar um processo semelhante para oCollector.summarizingInt
qual não existe originalmente no Kotlin.Não é minha culpa que eles tenham escolhido um exemplo trivial !!! Ok, aqui está um novo
summarizingInt
método para o Kotlin e uma amostra correspondente:Exemplo SummarizingInt
Mas é melhor criar uma função de extensão, 2 para corresponder aos estilos no Kotlin stdlib:
Agora você tem duas maneiras de usar as novas
summarizingInt
funções:E tudo isso produz os mesmos resultados. Também podemos criar esta extensão para trabalhar com
Sequence
tipos primitivos apropriados.Por diversão, compare o código Java JDK com o código personalizado Kotlin necessário para implementar esta sumarização.
fonte
.map { it.substring(1).toInt() }
: como você sabe, o tipo bem inferido é um dos poderes do kotlin.Existem alguns casos em que é difícil evitar ligações
collect(Collectors.toList())
ou algo semelhante. Nesses casos, você pode mudar mais rapidamente para um equivalente Kotlin usando funções de extensão, como:Em seguida, você pode simplesmente
stream.toList()
oustream.asSequence()
voltar para a API Kotlin. Um caso como oFiles.list(path)
força a entrarStream
quando você não deseja, e essas extensões podem ajudá-lo a voltar às coleções padrão e à API do Kotlin.fonte
Mais sobre preguiça
Vamos dar o exemplo de solução para "Calcular a soma dos salários por departamento" fornecida por Jayson:
Para tornar isso lento (ou seja, evitar criar um mapa intermediário na
groupBy
etapa), não é possível usá-loasSequence()
. Em vez disso, devemos usargroupingBy
efold
operar:Para algumas pessoas, isso pode ser ainda mais legível, já que você não está lidando com entradas do mapa: a
it.value
parte da solução também me confundiu no começo.Como esse é um caso comum e preferimos não escrever a
fold
cada vez, talvez seja melhor fornecer apenas umasumBy
função genérica emGrouping
:para que possamos simplesmente escrever:
fonte