Mesclando fluxos em Java

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.