Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Considering list elements that are added after filtered stream creation

Tags:

Given the following code:

List<String> strList = new ArrayList<>(Arrays.asList("Java","Python","Php"));

Stream<String> jFilter = strList.stream().filter(str -> str.startsWith("J"));

strList.add("JavaScript"); // element added after filter creation
strList.add("JQuery"); // element added after filter creation

System.out.println(Arrays.toString(jFilter.toArray())); 

which outputs:

[Java, JavaScript, JQuery]

Why do JavaScript and JQuery appear in the filtered result even though they were added after creating the filtered stream?

like image 828
mmuzahid Avatar asked Dec 22 '18 13:12

mmuzahid


People also ask

Does stream filter modify the original list?

That means list. stream(). filter(i -> i >= 3); does not change original list. All stream operations are non-interfering (none of them modify the data source), as long as the parameters that you give to them are non-interfering too.

Does filter modify the original array Java?

The filter() method creates a new array filled with elements that pass a test provided by a function. The filter() method does not execute the function for empty elements. The filter() method does not change the original array.

How do I filter a list based on another list in Java 8?

One of the utility method filter() helps to filter the stream elements that satisfy the provided criteria. The predicate is a functional interface that takes a single element as an argument and evaluates it against a specified condition.


2 Answers

Short Answer

You're assuming after this point:

Stream<String> jFilter = strStream.filter(str -> str.startsWith("J"));

That a new stream of the elements starting with "J" are returned i.e. only Java. However this is not the case;

streams are lazy i.e. they don't perform any logic unless told otherwise by a terminal operation.

The actual execution of the stream pipeline starts on the toArray() call and since the list was modified before the terminal toArray() operation commenced the result will be [Java, JavaScript, JQuery].

Longer Answer

here's part of the documentation which mentions this:

For well-behaved stream sources, the source can be modified before the terminal operation commences and those modifications will be reflected in the covered elements. For example, consider the following code:

 List<String> l = new ArrayList(Arrays.asList("one", "two"));
 Stream<String> sl = l.stream();
 l.add("three");
 String s = sl.collect(joining(" "));  

First a list is created consisting of two strings: "one"; and "two". Then a stream is created from that list. Next the list is modified by adding a third string: "three". Finally the elements of the stream are collected and joined together. Since the list was modified before the terminal collect operation commenced the result will be a string of "one two three". All the streams returned from JDK collections, and most other JDK classes, are well-behaved in this manner;

like image 97
Ousmane D. Avatar answered Sep 22 '22 12:09

Ousmane D.


Until the statement

System.out.println(Arrays.toString(jFilter.toArray()));

runs, the stream doesn't do anything. A terminal operation (toArray in the example) is required for the stream to be traversed and your intermediate operations (filter in this case) to be executed.

In this case, what you can do is, for example, capture the size of the list before adding other elements:

int maxSize = strList.size();
Stream<String> jFilter = strStream.limit(maxSize)
                                  .filter(str -> str.startsWith("J"));

where limit(maxSize) will not allow more than the initial elements to go through the pipeline.

like image 37
ernest_k Avatar answered Sep 21 '22 12:09

ernest_k