In this post, we will see how to add elements of a Stream into an existing List in Java 8.
1. Collectors.toCollection()
We can use a Collector
to add elements to an existing list as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import java.util.*; class ListUtil { // Generic function to add elements of a Stream into an existing list public static<T> void addToList(List<T> target, Stream<T> source) { source.collect(Collectors.toCollection(() -> target)); } public static void main (String[] args) { List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3)); addToList(list, Stream.of(4, 5)); System.out.println(list); } } |
Output:
[1, 2, 3, 4, 5]
Above program violates the requirement of a supplier, as the supplier returns an existing list each time it is called, instead of a new, empty list.
If a supplier that returns the same list is passed to Collectors#toCollection()
, each thread of parallel stream don’t get its own list for intermediate accumulation and appends its results to the specified list instead. So above code will fail if the stream is run in parallel.
One workaround is to call sequential()
on the stream before accumulating:
1 2 3 4 5 6 |
// Generic function to add elements of a Stream into an existing list public static<T> void addToList(List<T> target, Stream<T> source) { source.sequential() .collect(Collectors.toCollection(() -> target)); } |
2. Stream.forEachOrdered() + List.add()
We can add elements of a stream to an existing collection by using forEachOrdered()
along with a method reference to List#add()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import java.util.*; class ListUtil { // Generic function to add elements of a Stream into an existing list public static<T> void addToList(List<T> target, Stream<T> source) { source.forEachOrdered(target::add); } public static void main (String[] args) { List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3)); addToList(list, Stream.of(4, 5)); System.out.println(list); } } |
Output:
[1, 2, 3, 4, 5]
This approach works for both sequential and parallel streams but it does not get any benefit from concurrency as the method reference passed to forEachOrdered()
will be always executed sequentially.
3. List.addAll() + Collectors
We have seen that we can fill a list by repeatedly calling add()
method on every element of the Stream. But instead of calling add()
every time, a better approach would be to replace it by single call of addAll()
method as shown below. This approach, however, creates an intermediate list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import java.util.*; class ListUtil { // Generic function to add elements of a Stream into an existing list public static<T> void addToList(List<T> target, Stream<T> source) { target.addAll(source.collect(Collectors.toList())); } public static void main (String[] args) { List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3)); addToList(list, Stream.of(4, 5)); System.out.println(list); } } |
Output:
[1, 2, 3, 4, 5]
4. Stream.concat() + Collectors
This approach doesn’t mutate the original list but creates a new list containing elements from the speciied Stream and original list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import java.util.*; class ListUtil { // Generic function to add elements of a Stream into an existing list public static<T> List<T> addToList(List<T> target, Stream<T> source) { return Stream.concat(target.stream(), source) .collect(Collectors.toList()); } public static void main (String[] args) { List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3)); list = addToList(list, Stream.of(4, 5)); System.out.println(list); } } |
Output:
[1, 2, 3, 4, 5]
Thanks for reading.
Please use our online compiler to post code in comments. To contribute, get in touch with us.
Like us? Please spread the word and help us grow. Happy coding 🙂
Leave a Reply
gr8 job