Métodos da Stream API em Java: Sub-fluxos, Concatenação e Elementos Únicos
Além das operações básicas de filtragem e mapeamento, a Stream API oferece métodos para manipulações mais avançadas, como a criação de sub-fluxos baseados em condições, a combinação de múltiplos streams e a garantia da unicidade dos elementos.
Criando Sub-fluxos Condicionais
Os métodos takeWhile
e dropWhile
são operações intermediárias que permitem fatiar um stream com base em uma condição. Eles são particularmente úteis e eficientes em streams ordenados.
O Método takeWhile
O método takeWhile
seleciona elementos de um stream enquanto uma condição for atendida. Sua característica principal é que ele encerra a operação assim que encontra o primeiro elemento que não satisfaz a condição.
import java.util.stream.Stream;
// ...
Stream<Integer> numbers = Stream.of(-3, -2, -1, 0, 1, 2, 3, -4, -5);
numbers.sorted() // É crucial ordenar o stream primeiro
.takeWhile(n -> n < 0)
.forEach(n -> System.out.println(n));
Com o stream ordenado, o resultado será -5, -4, -3, -2, -1
.
Essa característica de "curto-circuito"(short-circuiting) torna takeWhile
muito mais eficiente que filter
em streams grandes e ordenados, pois ele não precisa percorrer todos os elementos.
O Método dropWhile
O método dropWhile
executa a tarefa oposta: ele descarta os elementos do stream enquanto a condição for atendida. Assim que encontra o primeiro elemento que não satisfaz a condição, ele para de descartar e retorna o restante do stream.
Stream<Integer> numbers = Stream.of(-3, -2, -1, 0, 1, 2, 3, -4, -5);
numbers.sorted()
.dropWhile(n -> n < 0)
.forEach(n -> System.out.println(n));
O resultado será 0, 1, 2, 3
, pois todos os números negativos no início do stream ordenado foram descartados.
Modificando a Composição do Stream
O Método concat
O método estático Stream.concat()
une os elementos de dois streams, retornando um novo stream que contém todos os elementos do primeiro, seguidos por todos os elementos do segundo. As fontes originais não são modificadas.
Stream<String> people1 = Stream.of("Tom", "Bob", "Sam");
Stream<String> people2 = Stream.of("Alice", "Kate", "Sam");
Stream.concat(people1, people2)
.forEach(n -> System.out.println(n));
O resultado será:
Tom Bob Sam Alice Kate Sam
Combinando Múltiplos Streams
Para combinar mais de dois streams, o concat
se torna verboso. Uma abordagem mais flexível é usar Stream.of
para criar um stream de streams e, em seguida, achatá-lo com flatMap
.
Stream<String> people1 = Stream.of("Tom", "Bob");
Stream<String> people2 = Stream.of("Alice", "Kate");
Stream<String> people3 = Stream.of("Sam", "John");
Stream.of(people1, people2, people3)
.flatMap(s -> s)
.forEach(System.out::println);
O resultado será:
Tom Bob Alice Kate Sam John
O Método distinct
O método distinct()
é uma operação intermediária que retorna um novo stream contendo apenas os elementos únicos do stream original.
Stream<String> people = Stream.of("Tom", "Bob", "Sam", "Tom", "Alice", "Kate", "Sam");
people.distinct()
.forEach(p -> System.out.println(p));
As duplicatas de "Tom" e "Sam" são removidas, resultando na saída:
Tom Bob Sam Alice Kate
Distinct com Objetos Customizados
É fundamental lembrar que a unicidade é determinada com base nos métodos equals()
e hashCode()
dos objetos. Se distinct()
for usado em um stream de objetos de uma classe customizada (como a classe Phone
nos temas anteriores), é essencial que essa classe sobrescreva adequadamente equals()
e hashCode()
. Sem isso, distinct()
comparará as referências de memória dos objetos, e não seus valores, provavelmente não removendo duplicatas como o esperado.
Resumo
takeWhile(condition)
: Retorna os elementos do início do stream enquanto a condição for verdadeira; para no primeiro elemento falso. É eficiente em streams ordenados.dropWhile(condition)
: Descarta os elementos do início do stream enquanto a condição for verdadeira e retorna o restante.Stream.concat(stream1, stream2)
: Cria um novo stream unindo dois streams existentes. Para unir mais de dois, o padrãoStream.of(...).flatMap(...)
é preferível.distinct()
: Retorna um stream contendo apenas os elementos únicos. Para objetos customizados, requer uma implementação correta dos métodosequals()
ehashCode()
.