Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stream till value found - optional [duplicate]

I want to terminate execution of stream when found first value. However, when I run code below it shows up that two methods are called even if value is present from first method.

public static void main(String[] args) {

        Optional<String> s = Stream.of(first(), second())
                .filter(Optional::isPresent)
                .findFirst()
                .flatMap(Function.identity());

        System.out.println(s.get());
    }

    private static Optional<String> first() {
        System.out.println("first");
        return Optional.of("someValue");
    }

    private static Optional<String> second() {
        System.out.println("second");
        return Optional.empty();
    }

I checked in docs that:

  1. isPresent

Return {@code true} if there is a value present, otherwise {@code false}.

  1. findFirst()

Returns an {@link Optional} describing the first element of this stream, or an empty {@code Optional} if the stream is empty. If the stream has no encounter order, then any element may be returned.

So the first condition it met, and the second seems to be also fulfilled cause it returns:

first
second
someValue

How to quit execution if first value present, and not execute second method?

like image 461
Bartek Avatar asked Jan 26 '23 18:01

Bartek


2 Answers

How to quit execution if first value present, and not execute second method?

Stream#findFirst is a short-circuiting terminal operation. However, the methods are being invoked when you call Stream.of(first(), second()). This can be proven with the following snippet:

Optional<String> s = Stream.of(first(), second())
                           .peek($ -> System.out.println("Hello World!"))
                           .filter(Optional::isPresent)
                           .findFirst()
                           .flatMap(Function.identity());

System.out.println(s.get());

Output:

first
second
Hello World!
someValue

To prevent first() and second() from executing when calling Stream#of, one solution would be to wrap them in a Supplier<Optional<String>>:

Stream<Supplier<Optional<String>>> stream = Stream.of(Test::first, Test::second);

Optional<String> s = stream.map(Supplier::get)
                           .filter(Optional::isPresent)
                           .findFirst()
                           .flatMap(Function.identity());

System.out.println(s.get());

Output:

first
someValue
like image 94
Jacob G. Avatar answered Jan 29 '23 08:01

Jacob G.


The problem is not that findFirst is not short-circuiting, but that Stream.of(first(), second()) causes immediate (not delayed) execution of first() and second. It's essentially evaluated as any ordinary expression.

In other words, even if your main method is just

final public static void main(String[] args) throws Exception {
    Stream.of(first(), second());
}

It would still execute first() and second() immediately.

like image 37
ernest_k Avatar answered Jan 29 '23 08:01

ernest_k