smash87
smash87

Reputation: 107

Apply filter to stream in Java

Given the following code I expect the output to be "-3-2-101231" since the filter should go through all the items of the stream, thus printing the item that is evaluating. When the allMatch function is evaluated it should print "1" since it is receiving a stream of positive integers, so it should short-circuit after finding that the first element is not negative.

Why does it print "-3-2-1011" instead of "-3-2-101231"?

    List<Integer> list = Arrays.asList(-3,-2,-1,0,1,2,3);
    Predicate<Integer> positive = i->{
        System.out.print(i);
        return i>0;
    };
    Predicate<Integer> negative = i->{
        System.out.print(i);
        return i<0;
    };

    list.stream().filter(positive).allMatch(negative);

Upvotes: 2

Views: 920

Answers (2)

Bojan Petkovic
Bojan Petkovic

Reputation: 2576

for '1' the value is false. and false && (anything else) will always be false, so it will not evaluate anything else.

Similarly with if statements for both true and false it does lazy evaluation:

public static void main(String[] args){
    System.out.println("Test1: False && anything else:");
    if (returnFalse() && returnTrue()) {

    }

    System.out.println("Test2: False || anything else:");
    if (returnFalse() || returnTrue()) {

    }
    System.out.println("Test3: True || anything else:");
    if (returnTrue() || returnTrue()) {

    }
    System.out.println("Test4: True && anything else:");
    if (returnTrue() && returnTrue()) {

    }


}
public static boolean returnFalse() {
    System.out.println("false");
    return false;
}

public static boolean returnTrue() {
    System.out.println("true");
    return true;
}

The results for this will be:

Test1: False && anything else:
false
Test2: False || anything else:
false
true
Test3: True || anything else:
true
Test4: True && anything else:
true
true

Upvotes: 0

erickson
erickson

Reputation: 269637

No, the filter should not go through every item in the stream. That would be a waste. As soon the allMatch() operation sees a non-negative item, the pipeline terminates.

There is no guarantee that all items in the stream will be filtered; the guarantee is that any item that is tested by allMatch() has first passed the preceding filter.

The output you see is -3 -2 -1 0 1 from the filter, then 1 from the final predicate, which terminates the process. There's no need to fetch 2 from the upstream filter operation when the terminal operation has already determined its result.

In the words of the documentation, Stream is

— Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.

And,

Processing streams lazily allows for significant efficiencies; in a pipeline such as the filter-map-sum example above, filtering, mapping, and summing can be fused into a single pass on the data, with minimal intermediate state. Laziness also allows avoiding examining all the data when it is not necessary; for operations such as "find the first string longer than 1000 characters", it is only necessary to examine just enough strings to find one that has the desired characteristics without examining all of the strings available from the source. (This behavior becomes even more important when the input stream is infinite and not merely large.)

Upvotes: 3

Related Questions