Слияние потоков в Java

Слияние потоков в Java

1. обзор

В этой быстрой статье мы объясним различные способы слияния JavaStreams, что не является интуитивно понятной операцией.

2. Использование простой Java

Класс JDK 8Stream имеет несколько полезных статических служебных методов. Давайте подробнее рассмотрим методconcat().

2.1. Слияние двухStreams

Самый простой способ объединить 2Streams - использовать статический методStream.concat():

@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. Объединение несколькихStreams

Когда нам нужно объединить более 2Streams,, все становится немного сложнее. Одна возможность состоит в том, чтобы объединить первые два потока, затем объединить результат со следующим и так далее.

Следующий фрагмент кода показывает это в действии:

@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()));
}

Как мы видим, такой подход становится невозможным для большего количества потоков. Конечно, мы можем создать промежуточные переменные или вспомогательные методы, чтобы сделать его более читабельным, но вот лучший вариант:

@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()));
}

Что происходит здесь:

  • Сначала мы создаем новыйStream, содержащий 4Streams,, что приводит кStream<Stream<Integer>>

  • Затем мыflatMap()преобразуем это вStream<Integer>, используя тождественную функцию

3. Использование StreamEx

StreamEx - это библиотека Java с открытым исходным кодом, которая расширяет возможности Java 8 Streams. Он использует классStreamEx как расширение интерфейса JDKStream.

3.1. ОбъединениеStreams

Библиотека StreamEx позволяет нам объединять потоки, используя метод экземпляраappend():

@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()));
}

Поскольку это метод экземпляра, мы можем легко связать его и добавить несколько потоков.

Обратите внимание, что мы также можем создатьList из потока, используяtoList(), если мы введем переменнуюresultingStream в типStreamEx.

3.2. Объединение потоков с использованиемprepend()

StreamEx также содержит метод, который добавляет элементы друг перед другом, называемыйprepend():

@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. Использование Jooλ

jOOλ - это библиотека, совместимая с JDK 8, которая предоставляет полезные расширения для JDK. Самая важная абстракция потока здесь называетсяSeq. Обратите внимание, что это последовательный и упорядоченный поток, поэтому вызовparallel() не будет иметь никакого эффекта.

4.1. Объединение потоков

Как и библиотека StreamEx, jOOλ имеет методappend():

@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()));
}

Кроме того, существует удобный методtoList(), если мы введем переменнуюresultingSeq в тип jOOλSeq.

4.2. Объединение потоков сprepend()

Как и ожидалось, поскольку существует методappend(), в jOOλ также есть методprepend():

@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. Заключение

Мы увидели, что объединение потоков относительно просто с использованием JDK 8. Когда нам нужно выполнить много слияний, может быть полезно использовать библиотеку StreamEx или jOOλ для удобства чтения.

Вы можете найти исходный кодover on GitHub.