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:
- https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
- https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html
- https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html
Happy Coding !!
Happy Learning !!