How to add elements of a Java8 stream into an existing List

codefx

Javadoc of Collector shows how to collect elements of a stream into a new List. Is there an one-liner that adds the results into an existing ArrayList?

Stuart Marks

NOTE: nosid's answer shows how to add to an existing collection using forEachOrdered(). This is a useful and effective technique for mutating existing collections. My answer addresses why you shouldn't use a Collector to mutate an existing collection.

The short answer is no, at least, not in general, you shouldn't use a Collector to modify an existing collection.

The reason is that collectors are designed to support parallelism, even over collections that aren't thread-safe. The way they do this is to have each thread operate independently on its own collection of intermediate results. The way each thread gets its own collection is to call the Collector.supplier() which is required to return a new collection each time.

These collections of intermediate results are then merged, again in a thread-confined fashion, until there is a single result collection. This is the final result of the collect() operation.

A couple answers from Balder and assylias have suggested using Collectors.toCollection() and then passing a supplier that returns an existing list instead of a new list. This violates the requirement on the supplier, which is that it return a new, empty collection each time.

This will work for simple cases, as the examples in their answers demonstrate. However, it will fail, particularly if the stream is run in parallel. (A future version of the library might change in some unforeseen way that will cause it to fail, even in the sequential case.)

Let's take a simple example:

List<String> destList = new ArrayList<>(Arrays.asList("foo"));
List<String> newList = Arrays.asList("0", "1", "2", "3", "4", "5");
newList.parallelStream()
       .collect(Collectors.toCollection(() -> destList));
System.out.println(destList);

When I run this program, I often get an ArrayIndexOutOfBoundsException. This is because multiple threads are operating on ArrayList, a thread-unsafe data structure. OK, let's make it synchronized:

List<String> destList =
    Collections.synchronizedList(new ArrayList<>(Arrays.asList("foo")));

This will no longer fail with an exception. But instead of the expected result:

[foo, 0, 1, 2, 3]

it gives weird results like this:

[foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0]

This is the result of the thread-confined accumulation/merging operations I described above. With a parallel stream, each thread calls the supplier to get its own collection for intermediate accumulation. If you pass a supplier that returns the same collection, each thread appends its results to that collection. Since there is no ordering among the threads, results will be appended in some arbitrary order.

Then, when these intermediate collections are merged, this basically merges the list with itself. Lists are merged using List.addAll(), which says that the results are undefined if the source collection is modified during the operation. In this case, ArrayList.addAll() does an array-copy operation, so it ends up duplicating itself, which is sort-of what one would expect, I guess. (Note that other List implementations might have completely different behavior.) Anyway, this explains the weird results and duplicated elements in the destination.

You might say, "I'll just make sure to run my stream sequentially" and go ahead and write code like this

stream.collect(Collectors.toCollection(() -> existingList))

anyway. I'd recommend against doing this. If you control the stream, sure, you can guarantee that it won't run in parallel. I expect that a style of programming will emerge where streams get handed around instead of collections. If somebody hands you a stream and you use this code, it'll fail if the stream happens to be parallel. Worse, somebody might hand you a sequential stream and this code will work fine for a while, pass all tests, etc. Then, some arbitrary amount of time later, code elsewhere in the system might change to use parallel streams which will cause your code to break.

OK, then just make sure to remember to call sequential() on any stream before you use this code:

stream.sequential().collect(Collectors.toCollection(() -> existingList))

Of course, you'll remember to do this every time, right? :-) Let's say you do. Then, the performance team will be wondering why all their carefully crafted parallel implementations aren't providing any speedup. And once again they'll trace it down to your code which is forcing the entire stream to run sequentially.

Don't do it.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Java 8 Stream add elements to list and sum

From Dev

How to add a single Key value to existing Java 8 stream?

From Dev

Java8 : How to filter a map of List value via stream

From Dev

How do I add a suffix (or prefix) elements of an existing list?

From Dev

How to add a value to the field of item in a list using Java 8 stream

From Dev

java8 stream collect different list

From Dev

How do I merge two List of String into one List without duplicate in Java8 stream

From Dev

how to add elements of a list

From Dev

How to create HashSet from List<Object> by taking multiple field values from object with Java8 Stream?

From Dev

Java add elements to list

From Java

How to add a list item to an existing unordered list?

From Java

Java8 Stream : Collect elements after a condition is met

From Dev

Java8 Stream : Collect elements after a condition is met

From Dev

Java 8: How to stream a list into a list of lists?

From Java

Java 8 list processing - add elements conditionally

From Dev

Java 8 list processing - add elements conditionally

From Dev

java8 map how to add some element to a list value simply

From Dev

How to reduce a stream into another stream in Java8?

From Dev

How to access elements of a stream in Java 8, or return one element of a stream?

From Dev

java.lang.ArrayIndexOutOfBoundsException: when using parallel stream to add elements to List

From Dev

How to add elements to a List in python?

From Dev

how to add elements to a list in python

From Dev

Java8 stream reducing list of objects with fluent API

From Dev

Count number of objects in a list using Java8 Stream

From Dev

List of specification combine using java8 stream

From Dev

Count number of objects in a list using Java8 Stream

From Dev

Java8 stream reducing list of objects with fluent API

From Dev

Java8 Stream API: Group a list into a custom class

From Dev

How to collect new List with existing List elements that have specific features via Java streams?

Related Related

  1. 1

    Java 8 Stream add elements to list and sum

  2. 2

    How to add a single Key value to existing Java 8 stream?

  3. 3

    Java8 : How to filter a map of List value via stream

  4. 4

    How do I add a suffix (or prefix) elements of an existing list?

  5. 5

    How to add a value to the field of item in a list using Java 8 stream

  6. 6

    java8 stream collect different list

  7. 7

    How do I merge two List of String into one List without duplicate in Java8 stream

  8. 8

    how to add elements of a list

  9. 9

    How to create HashSet from List<Object> by taking multiple field values from object with Java8 Stream?

  10. 10

    Java add elements to list

  11. 11

    How to add a list item to an existing unordered list?

  12. 12

    Java8 Stream : Collect elements after a condition is met

  13. 13

    Java8 Stream : Collect elements after a condition is met

  14. 14

    Java 8: How to stream a list into a list of lists?

  15. 15

    Java 8 list processing - add elements conditionally

  16. 16

    Java 8 list processing - add elements conditionally

  17. 17

    java8 map how to add some element to a list value simply

  18. 18

    How to reduce a stream into another stream in Java8?

  19. 19

    How to access elements of a stream in Java 8, or return one element of a stream?

  20. 20

    java.lang.ArrayIndexOutOfBoundsException: when using parallel stream to add elements to List

  21. 21

    How to add elements to a List in python?

  22. 22

    how to add elements to a list in python

  23. 23

    Java8 stream reducing list of objects with fluent API

  24. 24

    Count number of objects in a list using Java8 Stream

  25. 25

    List of specification combine using java8 stream

  26. 26

    Count number of objects in a list using Java8 Stream

  27. 27

    Java8 stream reducing list of objects with fluent API

  28. 28

    Java8 Stream API: Group a list into a custom class

  29. 29

    How to collect new List with existing List elements that have specific features via Java streams?

HotTag

Archive