Java Streams¶
Um stream descreve um pipeline sobre elementos. Não é uma coleção e não armazena seus próprios elementos. Um pipeline contém uma fonte, zero ou mais operações intermediárias e uma operação terminal.
Preguiça e único uso¶
Operações intermediárias como map e filter são avaliadas tardiamente; a travessia começa
quando uma operação terminal solicita resultados. Um stream não pode ser reutilizado após
uma operação terminal. O guia mais amplo sobre processamento tardio e imediato
cobre avaliação, ciclo de vida de recursos, snapshots e perigos de persistência.
record Reading(String sensor, double value) {}
List<String> activeSensors = readings.stream()
.filter(reading -> reading.value() > 0.0)
.map(Reading::sensor)
.distinct()
.sorted()
.toList();
A fonte não é modificada, mas a não interferência é responsabilidade do chamador: alterar a fonte durante a travessia ou alterar o estado compartilhado pelas operações pode tornar o comportamento inseguro ou não determinístico.
Categorias de operação¶
| Tipo | Exemplos | Resultado |
|---|---|---|
| Intermediário sem estado | map, filter |
Outro stream |
| Intermediário com estado | distinct, sorted, limit |
Outro stream, possivelmente armazenado em buffer |
| Curto-circuito | findFirst, anyMatch, limit |
Pode terminar cedo |
| Redução terminal | reduce, collect, count |
Resultado não-stream |
peek é principalmente um ponto de observação; não faça a correção depender de
seus efeitos colaterais, pois otimizações e curto-circuitos afetam quais elementos são
observados.
Redução¶
Map<String, Double> maximumBySensor = readings.stream()
.collect(Collectors.toMap(
Reading::sensor,
Reading::value,
Math::max));
Para uma redução paralela, a identidade e o acumulador/combinador devem satisfazer os requisitos de compatibilidade algébrica documentados. Associatividade é essencial; a adição de ponto flutuante não é matematicamente associativa; portanto, o reagrupamento pode mudar o arredondamento.
Streams paralelos¶
Paralelismo não é uma chave de velocidade gratuita. Funciona melhor para cargas suficientemente grandes, limitadas pela CPU, divisíveis, com operações sem estado e combinação barata. I/O bloqueante, mutação compartilhada, restrições de ordem de encontro, pequenas entradas e contenção podem torná-lo mais lento ou inseguro. Meça com um benchmark representativo.
Exercícios¶
- Substituir um stream aninhado por
flatMap. - Explicar diferença entre
findFirstefindAnyem paralelo. - Escreva um coletor que agrupe leituras por sensor e calcule estatísticas resumidas.