
Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:
Once the early-adopter seats are all used, the price will go up and stay at $33/year.
Last updated: March 27, 2025
In this quick article, we explain different ways of merging Java Streams – which is not a very intuitive operation.
The JDK 8 Stream class has some useful static utility methods. Let’s take a closer look at the concat() method.
The simplest way to combine 2 Streams is to use the static Stream.concat() method:
@Test
public void whenMergingStreams_thenResultStreamContainsElementsFromBoth() {
Stream<Integer> stream1 = Stream.of(1, 3, 5);
Stream<Integer> stream2 = Stream.of(2, 4, 6);
Stream<Integer> resultingStream = Stream.concat(stream1, stream2);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6),
resultingStream.collect(Collectors.toList()));
}
When we need to merge more than 2 Streams, things become a bit more complex. One possibility is to concatenate the first two streams, then concatenate the result with the next one and so on.
The next code snippet shows this in action:
@Test
public void given3Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream<Integer> stream1 = Stream.of(1, 3, 5);
Stream<Integer> stream2 = Stream.of(2, 4, 6);
Stream<Integer> stream3 = Stream.of(18, 15, 36);
Stream<Integer> resultingStream = Stream.concat(
Stream.concat(stream1, stream2), stream3);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36),
resultingStream.collect(Collectors.toList()));
}
As we can see, this approach becomes unfeasible for more streams. Of course, we can create intermediate variables or helper methods to make it more readable, but here is a better option:
@Test
public void given4Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream<Integer> stream1 = Stream.of(1, 3, 5);
Stream<Integer> stream2 = Stream.of(2, 4, 6);
Stream<Integer> stream3 = Stream.of(18, 15, 36);
Stream<Integer> stream4 = Stream.of(99);
Stream<Integer> 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()));
}
What happens here is:
StreamEx is an open-source Java library that extends possibilities of Java 8 Streams. It uses the StreamEx class as an enhancement to the JDK’s Stream interface.
The StreamEx library allows us to merge streams using the append() instance method:
@Test
public void given4Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream<Integer> stream1 = Stream.of(1, 3, 5);
Stream<Integer> stream2 = Stream.of(2, 4, 6);
Stream<Integer> stream3 = Stream.of(18, 15, 36);
Stream<Integer> stream4 = Stream.of(99);
Stream<Integer> 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()));
}
Since it is an instance method, we can easily chain it and append multiple streams.
Note that we could also create a List out of the stream by using toList() if we type the resultingStream variable to the StreamEx type.
StreamEx also contains a method that adds elements before one another called prepend():
@Test
public void given3Streams_whenPrepended_thenResultStreamContainsAllElements() {
Stream<String> stream1 = Stream.of("foo", "bar");
Stream<String> openingBracketStream = Stream.of("[");
Stream<String> closingBracketStream = Stream.of("]");
Stream<String> resultingStream = StreamEx.of(stream1)
.append(closingBracketStream)
.prepend(openingBracketStream);
assertEquals(
Arrays.asList("[", "foo", "bar", "]"),
resultingStream.collect(Collectors.toList()));
}
jOOλ is a JDK 8 compatible library that provides useful extensions to the JDK. The most important stream abstraction here is called Seq. Note that this is a sequential and ordered stream, so calling parallel() will have no effect.
Just like the StreamEx library, jOOλ has an append() method:
@Test
public void given2Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream<Integer> seq1 = Stream.of(1, 3, 5);
Stream<Integer> seq2 = Stream.of(2, 4, 6);
Stream<Integer> resultingSeq = Seq.ofType(seq1, Integer.class)
.append(seq2);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6),
resultingSeq.collect(Collectors.toList()));
}
Also, there is a convenience toList() method if we type the resultingSeq variable to the jOOλ Seq type.
As expected, since an append() method exists, there is also a prepend() method in jOOλ:
@Test
public void given3Streams_whenPrepending_thenResultStreamContainsAllElements() {
Stream<String> seq = Stream.of("foo", "bar");
Stream<String> openingBracketSeq = Stream.of("[");
Stream<String> closingBracketSeq = Stream.of("]");
Stream<String> resultingStream = Seq.ofType(seq, String.class)
.append(closingBracketSeq)
.prepend(openingBracketSeq);
Assert.assertEquals(
Arrays.asList("[", "foo", "bar", "]"),
resultingStream.collect(Collectors.toList()));
}
We saw that merging streams is relatively straightforward using JDK 8. When we need to do a lot of merging, it might be beneficial to use the StreamEx or jOOλ library for the sake of readability.