Pages

Tuesday, August 18, 2020

Java 8 Stream concat() - How To Merge Two Streams or More

1. Overview

In this article, you are going to learn the java 8 Stream api concat() method to merge two streams into one stream. In the end, merged streams or collections will have all elements from both streams.

Let us explore the different ways to merge the streams using java 8 stream api.

Java 8 Stream concat() - How To Merge Two Streams or More


2. Java 8 Stream API concat() Syntax

The following concat() syntax from api.

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

Stream.concat() method takes two Streams as input and returns one steam with all the values.

3. Java 8 Stream API concat() Rules

Stream.concat() 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.

The parallel stream is returned if either of the input streams in parallel. 

When the resulting stream is closed, the close handlers for both input streams are invoked.

If used concat() excessively in repeated concatenation or chaining then will cause deep call chains and generates StackOverflowException.

4. Example 1 To Merge Two Streams in Java 8

Let us write an example program to merge two streams into a single stream in java 8.

import java.util.stream.Stream;

public class StreamJoinTwoStreams {

	public static void main(String[] args) {

		// Creating stream 1
		Stream<Integer> stream1 = Stream.of(2, 4, 6, 8, 10);

		// Creating stream 2
		Stream<Integer> stream2 = Stream.of(1, 3, 5, 7, 9);

		// Merging two streams in java 8 using concat method
		Stream<Integer> mergedStream = Stream.concat(stream1, stream2);

		// printing merged stream values
		System.out.println("Merged stream values are ");
		mergedStream.forEach(even -> System.out.println(even));
	}

}

Output:

Merged stream values are 
2
4
6
8
10
1
3
5
7
9

Now, the resulting stream is containing both stream 1 and stream 2 values. This is how the concat() method produces the final stream with all values.

5. Example 2 To Merge Two Streams and Sort in Java 8


First, Create 2 streams with odd and even numbers. Now, use the concat() method to merge and sort the final stream using sorted() method on merged stream.

import java.util.stream.Stream;

public class StreamConcatSortExample {

	public static void main(String[] args) {

		// Creating odd numbers stream
		Stream<Integer> oddStream = Stream.of(1, 3, 5, 7, 9);

		// Creating even numbers stream
		Stream<Integer> evenStream = Stream.of(2, 4, 6, 8);

		// Merging two streams in java 8 using concat() method
		Stream<Integer> sortedStream = Stream.concat(oddStream, evenStream).sorted();

		// printing sorted merged stream values
		System.out.println("Sorted resulting stream values are ");
		sortedStream.forEach(even -> System.out.println(even));
	}

}
Output:

See the output and it is sorted in ascending order.
Sorted resulting stream values are 
1
2
3
4
5
6
7
8
9

6. Example 3 To Merge List of Strings in Java 8 


Java 8: merge lists with stream API using concat().

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

public class StreamMergeListStrings {

	public static void main(String[] args) {

		// Creating list 1 with string values
		List<String> list1 = Arrays.asList("hello", "how", "are", "you", "doing", "?");
		
		// Creating list 2 with string values
		List<String> list2 = Arrays.asList("I", "am", "doing", "great", "!");

		// Merging list of strings with concat method
		List<String> mergedList = Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList());
		
		// Merged list values
		System.out.println("Final merged list values are ");
		mergedList.forEach(finalValue -> System.out.println(finalValue));
	}

}
Output:
Final merged list values are 
hello
how
are
you
doing
?
I
am
doing
great
!

7. Example 4 To Merge Multiple Streams


As of now, you have seen how to merge two lists or streams into one stream. Next, learn how to merge more than 2 streams using concat() chaining concept. The objective is to club and collect all stream values into a single stream.

The below program does merge 5 streams into a single stream. Observe that used static imports to make the code readable.

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

import java.util.stream.Stream;

public class MultipleStreamMergeExample {

	public static void main(String[] args) {

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

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

		// Creating stream 3
		Stream<Integer> stream3 = Stream.of(7, 8, 9);

		// Creating stream 4
		Stream<Integer> stream4 = Stream.of(10, 11, 12);

		// Creating stream 5
		Stream<Integer> stream5 = Stream.of(13, 14, 15);

		Stream<Integer> final5StreamsMergeREsult = Stream.concat(stream1,concat(stream2, concat(stream3, concat(stream4, stream5))));

		// printing sorted merged stream values
		System.out.println("5 Streams merge result : ");
		final5StreamsMergeREsult.forEach(merge -> System.out.println(merge));

	}

}

Output:

The final stream obtained values from all 5 streams. So, merging multiple streams is possible. But this is the costly operation in concat() chaing and will make excessive deep calls. Hene, more chance to get the performance issues.
5 Streams merge result : 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

8. Example 5 To Merge Multiple Streams using flatMap() method


The above program is not readable and difficult to understand. Instead of using the concat() method, Stream api has another utility method flatMap() that does the merging the multiple streams.

Look at the below example, you will understand how to use flatMap() to merge the multiple streams.

Let us first create a new stream from all 5 streams using Stream.of() method.
import java.util.stream.Stream;

public class MultipleStreamMergeFlatMapExample {

	public static void main(String[] args) {

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

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

		// Creating stream 3
		Stream<Integer> stream3 = Stream.of(7, 8, 9);

		// Creating stream 4
		Stream<Integer> stream4 = Stream.of(10, 11, 12);

		// Creating stream 5
		Stream<Integer> stream5 = Stream.of(13, 14, 15);

		// Creating a new string from all 5 streams using Stream.of() method.
		Stream<Stream<Integer>> finalStream = Stream.of(stream1, stream2, stream3, stream4, stream5);

		Stream<Integer> mergedStream = finalStream.flatMap(stream -> stream.map( v -> v));

		// printing sorted merged stream values
		System.out.println("merge with flatmap : ");
		mergedStream.forEach(merge -> System.out.println(merge));

	}

}

Output:
5 Streams merge result : 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

9. Example 6 To Merge Streams and Retain Unique Objects


Example to remove the duplicates and keep only distinct values after merging two streams.
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class RetainDistinctValues {

	public static void main(String[] args) {

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

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

		// Removing duplicate elemetns
		List<Integer> uniqueValues = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
		
		// printing sorted merged stream values
		System.out.println("Removed duplicates after merge");
		uniqueValues.forEach(merge -> System.out.println(merge));
		

	}

}
Output:
Removed duplicates after merge
1
2
3
4
5
6

10. Example 7 To Merge Streams with Custom Objects


Let us create a user-defined Employee class with id, name fields. Add duplicate emp id to multiple objects to the stream.  We added 100, 101, 103 id's are duplicates and using concat() and filter() methods to remove the duplicate objects.
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CusomObjectsDistinctValues {

	public static void main(String[] args) {

		// Create Employee stream 1
		Stream<Employee> stream1 = getEmployeStream1();

		// Create Employee stream 2
		Stream<Employee> stream2 = getEmployeStream2();

		// Removing duplicate elemetns
		List<Employee> uniqueValues = Stream.concat(stream1, stream2).filter(distinctByKey(Employee::getId)).collect(Collectors.toList());

		// printing sorted merged stream values
		System.out.println("Removed duplicates after merge");
		uniqueValues.forEach(merge -> System.out.println(merge));

	}
	
	public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor)
    {
        Map<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

	private static Stream<Employee> getEmployeStream1() {

		Employee e1 = new Employee(100, "Navamsha");
		Employee e2 = new Employee(101, "dhsamsha");
		Employee e3 = new Employee(103, "Chitra");

		return Stream.of(e1, e2, e3);
	}

	private static Stream<Employee> getEmployeStream2() {

		Employee e1 = new Employee(100, "Navamsha");
		Employee e2 = new Employee(101, "dhsamsha");
		Employee e3 = new Employee(102, "Varshad");
		Employee e4 = new Employee(103, "Chitra");

		return Stream.of(e1, e2, e3, e4);
	}
}

class Employee {
	private int id;
	private String name;

	public Employee(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + "]";
	}

}
Output:
Removed duplicates after merge
Employee [id=100, name=Navamsha]
Employee [id=101, name=dhsamsha]
Employee [id=103, name=Chitra]
Employee [id=102, name=Varshad]

11. Example 8 concat() with stream has already been operated upon or closed


Look at the below code and created two streams. Next, invoked the concat() method with two streams. At last, trying to access the elements of stream1 using the forEach() method. 
import java.util.stream.Stream;

public class StreamConcatException {

	public static void main(String[] args) {

		// Creating stream 1
		Stream<Integer> stream1 = Stream.of(1, 1, 1);

		// Creating stream 2
		Stream<Integer> stream2 = Stream.of(2, 2, 2);

		// Merging two streams in java 8 using concat() method
		Stream<Integer> resultStream = Stream.concat(stream1, stream2);

		// printing stream1 values
		System.out.println("Stream 1 values are ");
		stream1.forEach(even -> System.out.println(even));
	}

}

Output:
Stream 1 values are 
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 limit.StreamJoinTwoStreams.main(StreamJoinTwoStreams.java:21)

Observe output saying stream1 is already closed and you can not make any stream operations on it. 

But, we did not call any terminal operation to close the stream. We have just called the concat() method. concat() method closes the passed two streams internally by calling Stream.onClose(Streams.composedClose(a, b)) method.

12. Conclusion

In this article, You've seen in-depth about the Stream.concat() method.

And also shown how to merge two or more streams using concat() and flatMap() methods.

All examples are shown are over GitHub.

GitHub all Examples

Stream API concat()

Merging streams

Java 8 - distinctBy property

No comments:

Post a Comment

Please do not add any spam links in the comments section.