How to reduce stream to single value if it size is 1 or to a default value if size > 1

Viewed 421

I have a stream with 0..n values. I want to:

  • return value from stream if stream count() is 1
  • return defaultValue if stream count() is 0 or > 1

Code what I developed resolved this problem:

String result = Stream.of("someValue")
   .map(Optional::of)                  // reduce desn't accept `null`
   .reduce((a, b) -> Optional.empty()) // instead `null`
   .flatMap(Function.identity())       // unpack `Optional`
   .orElseGet(() -> "defaultValue");   // if returned our optional is empty,
                                       // so we can return default value
System.out.println(result);

But this hack with wraping stream values into Optional isn't easy to understand (in my opinion). Do you know any other solution, which bases on stream API? How to write this in easy to read way?

2 Answers

I don't think you need to wrap elements into optionals at all? You can just do:

yourStream.limit(2).reduce((x, y) -> defaultValue).orElse(defaultValue)

limit(2), so that you don't consume the whole stream. You just need to check at most 2 elements.

If you want a more complex solution, use the following:

<T> T exactlyOneOr(Stream<T> stream, T defaultValue) {
  List<T> reference = new ArrayList<>(1);
  boolean exactlyOne = spliterator.tryAdvance(reference::add)
      && !spliterator.tryAdvance(x -> {});
  return exactlyOne ? reference.get(0) : defaultValue;
}

It is still performant and you shouldn't have much trouble understanding it. If you do, just ask for clarifications.

Related