Mesclando fluxos em Java
1. Visão geral
Neste artigo rápido, explicamos diferentes maneiras de mesclar JavaStreams - o que não é uma operação muito intuitiva.
2. Usando Plain Java
A classe JDK 8Stream possui alguns métodos utilitários estáticos úteis. Vamos dar uma olhada no métodoconcat().
2.1. Mesclando doisStreams
A maneira mais simples de combinar 2Streams é usar o métodoStream.concat() estático:
@Test
public void whenMergingStreams_thenResultStreamContainsElementsFromBoth() {
Stream stream1 = Stream.of(1, 3, 5);
Stream stream2 = Stream.of(2, 4, 6);
Stream resultingStream = Stream.concat(stream1, stream2);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6),
resultingStream.collect(Collectors.toList()));
}
2.2. Mesclando váriosStreams
Quando precisamos mesclar mais de 2Streams, as coisas se tornam um pouco mais complexas. Uma possibilidade é concatenar os dois primeiros fluxos, concatenar o resultado com o próximo e assim por diante.
O próximo trecho de código mostra isso em ação:
@Test
public void given3Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream stream1 = Stream.of(1, 3, 5);
Stream stream2 = Stream.of(2, 4, 6);
Stream stream3 = Stream.of(18, 15, 36);
Stream resultingStream = Stream.concat(
Stream.concat(stream1, stream2), stream3);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36),
resultingStream.collect(Collectors.toList()));
}
Como podemos ver, essa abordagem se torna inviável para mais fluxos. Obviamente, podemos criar variáveis intermediárias ou métodos auxiliares para torná-los mais legíveis, mas aqui está uma opção melhor:
@Test
public void given4Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream stream1 = Stream.of(1, 3, 5);
Stream stream2 = Stream.of(2, 4, 6);
Stream stream3 = Stream.of(18, 15, 36);
Stream stream4 = Stream.of(99);
Stream resultingStream = Stream.of(
stream1, stream2, stream3, stream4)
.flatMap(i -> i);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36, 99),
resultingStream.collect(Collectors.toList()));
}
O que acontece aqui é:
-
Primeiro criamos um novoStream contendo 4Streams, que resulta em umStream<Stream<Integer>>
-
Então nósflatMap() isso em umStream<Integer> usando a função de identidade
3. Usando StreamEx
StreamEx é uma biblioteca Java de código aberto que amplia as possibilidades do Java 8 Streams. Ele usa a classeStreamEx como um aprimoramento da interfaceStream do JDK.
3.1. MesclandoStreams
A biblioteca StreamEx nos permite mesclar streams usando o método de instânciaappend():
@Test
public void given4Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream stream1 = Stream.of(1, 3, 5);
Stream stream2 = Stream.of(2, 4, 6);
Stream stream3 = Stream.of(18, 15, 36);
Stream stream4 = Stream.of(99);
Stream resultingStream = StreamEx.of(stream1)
.append(stream2)
.append(stream3)
.append(stream4);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36, 99),
resultingStream.collect(Collectors.toList()));
}
Como é um método de instância, podemos facilmente encadeá-lo e anexar vários fluxos.
Observe que também podemos criar umList fora do fluxo usandotoList() se digitarmos a variávelresultingStream para o tipoStreamEx.
3.2. Mesclando fluxos usandoprepend()
StreamEx também contém um método que adiciona elementos um antes do outro, chamadoprepend():
@Test
public void given3Streams_whenPrepended_thenResultStreamContainsAllElements() {
Stream stream1 = Stream.of("foo", "bar");
Stream openingBracketStream = Stream.of("[");
Stream closingBracketStream = Stream.of("]");
Stream resultingStream = StreamEx.of(stream1)
.append(closingBracketStream)
.prepend(openingBracketStream);
assertEquals(
Arrays.asList("[", "foo", "bar", "]"),
resultingStream.collect(Collectors.toList()));
}
4. Usando Jooλ
jOOλ é uma biblioteca compatível com JDK 8 que fornece extensões úteis para o JDK. A abstração de fluxo mais importante aqui é chamadaSeq. Observe que este é um fluxo sequencial e ordenado, portanto, chamarparallel() não terá efeito.
4.1. Mesclando Streams
Assim como a biblioteca StreamEx, jOOλ tem um métodoappend():
@Test
public void given2Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream seq1 = Stream.of(1, 3, 5);
Stream seq2 = Stream.of(2, 4, 6);
Stream resultingSeq = Seq.ofType(seq1, Integer.class)
.append(seq2);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6),
resultingSeq.collect(Collectors.toList()));
}
Além disso, há um método de conveniênciatoList() se digitarmos a variávelresultingSeq para o tipo jOOλSeq.
4.2. Mesclando Streams comprepend()
Como esperado, uma vez que existe um métodoappend(), também há um métodoprepend() em jOOλ:
@Test
public void given3Streams_whenPrepending_thenResultStreamContainsAllElements() {
Stream seq = Stream.of("foo", "bar");
Stream openingBracketSeq = Stream.of("[");
Stream closingBracketSeq = Stream.of("]");
Stream resultingStream = Seq.ofType(seq, String.class)
.append(closingBracketSeq)
.prepend(openingBracketSeq);
Assert.assertEquals(
Arrays.asList("[", "foo", "bar", "]"),
resultingStream.collect(Collectors.toList()));
}
5. Conclusão
Vimos que a fusão de fluxos é relativamente direta usando o JDK 8. Quando precisamos fazer muita fusão, pode ser benéfico usar a biblioteca StreamEx ou jOOλ para facilitar a leitura.
Você pode encontrar o código-fonteover on GitHub.