3

I have a Stream<Optional<T>>.

I want to use the findFirst()/findAny() methods to return a value. However I want to throw different exceptions depending on the Stream being empty or not.

Stream<Optional<MyType>> stream = ...;
return stream
  .flatMap(Optional::stream)
  .findFirst()
  .orElseThrow(() -> {
    // I want to know here if the stream was empty or not but flatMap forgot that
});

The logic should still be lazily evaluated, once I found a non-empty Optional, the stream should not be further progressed and the value should be returned early.

I tried to mutate a variable in the upper scope in a map to read from it but variables used in Lambda expressions have to be final.

boolean empty = true;
Stream<Optional<MyType>> stream = ...;
return stream
  .map(t -> {
    empty = false; //error, must be final
    return t;
  })
  .flatMap(Optional::stream)
  .findFirst()
  .orElseThrow(() -> {
    if (empty) {
      throw ...;
    } else {
      throw ...;
    }
});

I tried to write a plain for loop but this does not work with streams:

boolean empty = true;
Stream<Optional<MyType>> stream = ...;
for (Optional<MyType> o: stream) { //error, can't for loop on type Stream<...>
  if (o.isEmpty()) {
    empty = false;
  } else {
    return o.get();
  }
}
if (empty) {
  throw ...;
} else {
  throw ...;
}

What can I do here?

Urben
  • 65
  • 5
  • From what I know, you can't exit a stream early and return a value. So the way is to use a classic for loop on a collection (maybe convert your stream to a list). There you can implement the logic mentioned above. – happy songs Jan 10 '23 at 11:45
  • A classic loop isn't necessary, but you will need to collect the stream into a `List`. You can then use its `isEmpty()` method for the "stream is empty case", and get a new stream from it otherwise for the remainder of the code. – Rob Spoor Jan 10 '23 at 12:10
  • @RobSpoor Would I get the closest to my intention to not always get all elements if I used `stream.iterator()` instead of collecting it? I just would have to make sure to only use the iterator once, in one for loop, right? – Urben Jan 10 '23 at 12:14
  • @happysongs you _can_ exit a stream early using `findFirst()` or `findAny()` etc. - this basically exists the stream on the first value encountered. – Thomas Jan 10 '23 at 12:40
  • "Variables used in Lambda expressions have to be final" - that's true but the values of those don't have to be immutable. So you _could_ try to replace `boolean empty` with `AtomicBoolean empty` - there might be other considerations that I'm missing atm though. – Thomas Jan 10 '23 at 12:41
  • @Thomas , I meant you can't exit a stream while filtering for present optionals. It could be done with a classic for loop. – happy songs Jan 10 '23 at 12:49
  • @happysongs I'm not sure I get your meaning but you wouldn't wait for the filtering of all elements to be done before further processing, i.e. with `findFirst()` you'd exit stream processing which includes filtering - so if the first element is non-empty there'd be only one filter operation done (assuming the stream is not a parallel one). This is quite similar to how a classic loop would do it (think of the loop body as being like the stream operations until a terminal operation is encountered). – Thomas Jan 10 '23 at 12:56
  • @Thomas, it's correct what you're saying. What I understood from the question is that he wants to get the first element which is present. If one is found, it should immediately return it and stop checking `isPresent()` for other elements in the stream. In case all optionals are empty, it throws an exception. He doesn't want to filter the entire stream before calling `findFirst()`. – happy songs Jan 10 '23 at 13:53
  • @happysongs exactly. Assuming the stream is sequential this should amount to an ordered processing where filtering only happens until the first element has been found. In a parallel stream this should come down to local sequential processing until each batch has a first element and then checking which one was the actual first. – Thomas Jan 10 '23 at 14:04
  • 1
    Why do you have a `Stream>` to begin with? That’s the real problem. – Holger Jan 12 '23 at 09:39

0 Answers0