Java 8 – Stream concat() method for merging elements

In this article, we will discuss Stream’s concat() method which is used to merge elements from 2 or more Streams into single Stream consisting of all elements from merged Streams

1. Stream concat() method :

  • This Stream method creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream
  • The resulting stream is ordered if both of the input streams are ordered
  • Or the resulting stream is parallel if either of the input streams is parallel
  • When the resulting stream is closed, the close handlers for both input streams are invoked
  • Method signature :- static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)



2. Merging 2 Stream elements :

  • There are 2 Stream of elements defined using Stream.of() method
  • We are going to concat/merge these Stream elements into one Stream
  • Finally, printing merged Stream elements using forEach() method to the console
package net.bench.resources.stream.concat.example;

import java.util.stream.Stream;

public class Merge2StreamUsingConcatMethod {

	public static void main(String[] args) {

		// Stream 1
		Stream<Integer> stream1 = Stream.of(2,4,5,7,9);

		// Stream 2
		Stream<Integer> stream2 = Stream.of(1,3,6,8,10);

		// Merge 2 streams using Stream.concat()
		Stream<Integer> resultStream = Stream.concat(stream1, stream2);

		// print to console
		System.out.println("Merged Streams :- \n");
		resultStream.forEach(num -> System.out.println(num));
	}
}

Output:

Merged Streams :- 

2
4
5
7
9
1
3
6
8
10

3. Merging and Sorting 2 Stream elements :

  • There are 2 Stream of elements defined using Stream.of() method
  • We are going to concat/merge these Stream elements into one Stream and then sort the merged numbers according to ascending order
  • Finally, printing sorted/merged Stream elements using forEach() method to the console
package net.bench.resources.stream.concat.example;

import java.util.stream.Stream;

public class Merge2StreamAndSort {

	public static void main(String[] args) {

		// Stream 1
		Stream<Integer> stream1 = Stream.of(2,4,5,7,9);

		// Stream 2
		Stream<Integer> stream2 = Stream.of(1,3,6,8,10);

		// Merge 2 streams using Stream.concat()
		Stream<Integer> resultStream = Stream
				.concat(stream1, stream2)
				.sorted();

		// print to console
		System.out.println("Sorted Streams after merging :- \n");
		resultStream.forEach(num -> System.out.println(num));
	}
}

Output:

Sorted Streams after merging :- 

1
2
3
4
5
6
7
8
9
10

4. Merging 2 Collections :

  • There are 2 List of String elements defined
  • First, get Stream for both List of Strings using stream() method and concat/merge these Stream elements into one Stream using concat() method and then collect it to List using collect(Collectors.toList()) method
  • Finally, print merged List using forEach() method to the console
package net.bench.resources.stream.concat.example;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Merge2Collection {

	public static void main(String[] args) {

		// list of Strings 1
		List<String> strList1 = Arrays.asList(
				"Rahul",
				"Laxman",
				"Sehwag",
				"Sachin",
				"Sourav"
				);

		// list of Strings 2
		List<String> strList2 = Arrays.asList(
				"Kohli",
				"Dhoni",
				"Yuvraj",
				"Kaif"
				);

		// get Stream for both Collection 1 and 2
		Stream<String> stream1 = strList1.stream();
		Stream<String> stream2 = strList2.stream();

		// Merge 2 String list
		List<String> mergedList = Stream
				.concat(stream1, stream2)
				.collect(Collectors.toList());

		// print to console
		System.out.println("Merged 2 Collections :- \n");
		mergedList.stream().forEach(num -> System.out.println(num));
	}
}

Output:

Merged 2 Collections :- 

Rahul
Laxman
Sehwag
Sachin
Sourav
Kohli
Dhoni
Yuvraj
Kaif

5. Merging multiple Stream elements :

  • There are 4 Stream of elements defined using Stream.of() method
  • In the previous examples, we dealt with only 2 Stream but in this example we are dealing with 4 Stream elements
  • Of course, with concat() method we can merge these 4 Stream elements, as shown in the below example
  • But concatenating/merging multiple Streams into one Stream results into performance degradation
  • Note: for code readability purpose, we are using static import of concat() method
package net.bench.resources.stream.concat.example;

import java.util.stream.Stream;
import static java.util.stream.Stream.concat;

public class MergeMultipleStreams {

	public static void main(String[] args) {

		// Stream 1
		Stream<Integer> stream1 = Stream.of(1,2,3);

		// Stream 2
		Stream<Integer> stream2 = Stream.of(1,4,5);

		// Stream 3
		Stream<Integer> stream3 = Stream.of(2,6,7);

		// Stream 4
		Stream<Integer> stream4 = Stream.of(3,8,9);

		// Merge multiple streams using Stream.concat()
		Stream<Integer> resultStream = Stream
				.concat(stream1, 
						concat(stream2, 
								concat(stream3, stream4)));

		// print to console
		System.out.println("Multiple Merged Streams elements :- \n");
		resultStream.forEach(num -> System.out.println(num));
	}
}

Output:

Multiple Merged Streams elements :- 

1
2
3
1
4
5
2
6
7
3
8
9

6. Merging multiple Streams and get distinct/unique elements :

  • We are going to reuse previous example where we are concatenating/merging multiple Stream into single Stream using Stream.concat() method
  • Resulting Stream elements contains duplicate values
  • To remove duplicate values from resulting Stream, we are going to invoke distinct() method on the resulting Stream at the end
  • Finally, printing unique merged Stream elements to the console using forEach() method
package net.bench.resources.stream.concat.example;

import static java.util.stream.Stream.concat;

import java.util.stream.Stream;

public class MergeMultipleStreamsAndRemoveDuplicates {

	public static void main(String[] args) {

		// Stream 1
		Stream<Integer> stream1 = Stream.of(1,2,3); // 3 elements

		// Stream 2
		Stream<Integer> stream2 = Stream.of(1,4,5); // 3 elements

		// Stream 3
		Stream<Integer> stream3 = Stream.of(2,6,7); // 3 elements

		// Stream 4
		Stream<Integer> stream4 = Stream.of(3,8,9); // 3 elements

		// Merge multiple streams and remove duplicates
		Stream<Integer> resultStream = Stream
				.concat(stream1, 
						concat(stream2, 
								concat(stream3, stream4)))
				.distinct(); // removes duplicates

		// print to console
		System.out.println("Distinct elements after Multiple Streams Merged :- \n");
		resultStream.forEach(num -> System.out.println(num));
	}
}

Output:

Distinct elements after Multiple Streams Merged :- 

1
2
3
4
5
6
7
8
9

7. Merging multiple Stream elements using flapMap() method :

  • As we have seen in the previous example that merging multiple Streams using concat() method results into performance degradation
  • Therefore a better approach is to use Stream.of() and Stream.flatMap() methods for merging
  • Stream.of() method helps to merges multiple Stream elements and Stream.flatMap() helps to flattens, as shown in the below example
package net.bench.resources.stream.concat.example;

import java.util.stream.Stream;

public class MergeMultipleStreamsUsingFlatMap {

	public static void main(String[] args) {

		// Stream 1
		Stream<Integer> stream1 = Stream.of(1,2,3);

		// Stream 2
		Stream<Integer> stream2 = Stream.of(1,4,5);

		// Stream 3
		Stream<Integer> stream3 = Stream.of(2,6,7);

		// Stream 4
		Stream<Integer> stream4 = Stream.of(3,8,9);

		// Merge multiple streams using Stream.of()
		Stream<Stream<Integer>> mergedStream = Stream
				.of(stream1, stream2, stream3, stream4);

		// flat the merged streams using flatMap() method
		Stream<Integer> resultStream = mergedStream
				.flatMap(stream -> stream.map(num -> num));

		// print to console
		System.out.println("Multiple Merged Streams using flatMap() :- \n");
		resultStream.forEach(num -> System.out.println(num));
	}
}

Output:

Multiple Merged Streams using flatMap() :- 

1
2
3
1
4
5
2
6
7
3
8
9

8. Merging 2 List of Students :

  • There are 2 list of Students defined where 1st contains 5 Students and 2nd list contains 4 Students
  • Our task it to merge both Student list into single list removing duplicates, if any
  • To achieve this, we have to get Stream for both Student lists and concatenate/merge using concat() method at the same time apply filter to remove duplicate Student based on his/her name by passing Predicate
  • Predicate:- create a Set to store Student name which Stores only unique element, thereby removing duplicate Students
  • Finally, collect into new List using collect() method after applying above Predicate

Student.java

package net.bench.resources.stream.concat.example;

public class Student {

	// member variables
	private int rollNumber;
	private String name;
	private int age;

	// constructors
	public Student(int rollId, String name, int age) {
		super();
		this.rollNumber = rollId;
		this.name = name;
		this.age = age;
	}

	// getters & setters
	public int getRollId() {
		return rollNumber;
	}
	public void setRollId(int rollId) {
		this.rollNumber = rollId;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}

	// toString()
	@Override
	public String toString() {
		return "Student [rollNumber=" + rollNumber 
				+ ", name=" + name 
				+ ", age=" + age 
				+ "]";
	}
}

MergeStreamOfStudents.java

package net.bench.resources.stream.concat.example;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import net.bench.resources.stream.min.max.example.Student;

public class MergeStreamOfStudents {

	public static void main(String[] args) {

		// 1. list of students
		List<Student> batchOneStudents = Arrays.asList(
				new Student(1, "Viraj", 17),
				new Student(2, "Krish", 18),
				new Student(3, "Rishi", 16),
				new Student(4, "Suresh", 23),
				new Student(5, "Aditya", 21)
				);

		// 2. list of students
		List<Student> batchTwoStudents = Arrays.asList(
				new Student(9, "Naresh", 18),
				new Student(8, "Rajesh", 19),
				new Student(7, "Abdul", 17),
				new Student(4, "Suresh", 23)
				);

		// get stream for 2 Lists
		Stream<Student> studentStream1 = batchOneStudents.stream();
		Stream<Student> studentStream2 = batchTwoStudents.stream();

		// Merge and filter duplicate Student
		Set<String> nameSet = new HashSet<>();
		List<Student> mergedStudents = Stream
				.concat(studentStream1, studentStream2)
				.filter(student -> nameSet.add(student.getName()))
				.collect(Collectors.toList());

		// print to console
		System.out.println("Merged unique Students :- \n");
		mergedStudents.forEach(num -> System.out.println(num));
	}
}

Output:

Merged unique Students :- 

Student [rollNumber=1, name=Viraj, age=17]
Student [rollNumber=2, name=Krish, age=18]
Student [rollNumber=3, name=Rishi, age=16]
Student [rollNumber=4, name=Suresh, age=23]
Student [rollNumber=5, name=Aditya, age=21]
Student [rollNumber=9, name=Naresh, age=18]
Student [rollNumber=8, name=Rajesh, age=19]
Student [rollNumber=7, name=Abdul, age=17]

9. Exception – stream has already been operated upon or closed :

  • Operating on the original Streams that’s to be concatenated/merged or resulting merged Stream twice (i.e.; more than once) throws IllegalStateException
package net.bench.resources.stream.concat.example;

import java.util.stream.Stream;

public class OperatingOnStreamTwice {

	public static void main(String[] args) {

		// Stream 1
		Stream<Integer> stream1 = Stream.of(1,2,3);

		// Stream 2
		Stream<Integer> stream2 = Stream.of(1,4,5);

		// Merge multiple streams & get count
		Stream<Integer> resultStream = Stream
				.concat(stream1, stream2);
		System.out.println("No. of distinct elements :- " 
				+ resultStream.count());

		// accessing 2nd time the resulting Stream
		resultStream.forEach(System.out::println);
	}
}

Output:

No. of distinct elements :- 6
Exception in thread "main" java.lang.IllegalStateException: 
stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at net.bench.resources.stream.concat.example.OperatingOnStreamTwice
.main(OperatingOnStreamTwice.java:22)

References:

Happy Coding !!
Happy Learning !!

Java 8 - Stream to Array conversion using toArray() method
Java 8 - Stream collect() method with examples