Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transfer a List into a Java Stream,and then delete a element of the List.Some errors occur

List<String> list = new ArrayList(){{add("apple");add("banana");add("orange");}};
Stream<String> stringStream = list.stream();
stringStream.forEach(
        m->{
            if (m.equals("banana")){
                list.remove("banana");
            }
        }
);
System.out.println(stringStream.count());

Fianlly, the console prints a NullPointerException error. The CoreJava says that we shouldn't modify the Collection which will return back to the stream after modified. And I don't have a clear understanding of the principle.

like image 212
jfhuang Avatar asked Nov 20 '19 06:11

jfhuang


People also ask

How do I remove items from my Stream List?

Once we've finished operating on the items in the stream, we can remove them using the same Predicate we used earlier for filtering: itemList. removeIf(isQualified); Internally, removeIf uses an Iterator to iterate over the list and match the elements using the predicate.

How do I remove an element from a collection in Java?

An element can be removed from a Collection using the Iterator method remove(). This method removes the current element in the Collection. If the remove() method is not preceded by the next() method, then the exception IllegalStateException is thrown.


2 Answers

The Consumer passed to forEach must be non-interfering. The reasoning is explained below.

Non-interference

Streams enable you to execute possibly-parallel aggregate operations over a variety of data sources, including even non-thread-safe collections such as ArrayList. This is possible only if we can prevent interference with the data source during the execution of a stream pipeline. Except for the escape-hatch operations iterator() and spliterator(), execution begins when the terminal operation is invoked, and ends when the terminal operation completes. For most data sources, preventing interference means ensuring that the data source is not modified at all during the execution of the stream pipeline. The notable exception to this are streams whose sources are concurrent collections, which are specifically designed to handle concurrent modification. Concurrent stream sources are those whose Spliterator reports the CONCURRENT characteristic.

(Source)

BTW, your stringStream.count() would have failed even if the previous stringStream.forEach() statement did not, since forEach (as any terminal operation) consumes the Stream, and a Stream cannot be consumed twice.

The correct way to achieve what you were trying to do is to filter the original List and create a new List:

List<String> filtered = 
    list.stream()
        .filter(m->!m.equals("banana"))
        .collect(Collectors.toList());
like image 135
Eran Avatar answered Oct 13 '22 19:10

Eran


You can't use streams to remove elements from a list, but you can use lambda expression by calling removeIf().

List<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "orange"));

list.removeIf(m -> m.equals("banana")); // or: list.removeIf("banana"::equals)

System.out.println(list); // prints: [apple, orange]
System.out.println(list.size()); // prints: 2
like image 26
Andreas Avatar answered Oct 13 '22 20:10

Andreas