4

I have some experience with C#'s LINQ and am trying to learn Java 8's stream API. Now in LINQ I regularly use the Single() Method, which picks the one-and-only object out of a sequence and throws an exception if there isn't a one-and-only object in the stream.

Now:

  • Am I correct in thinking Java 8's streams don't have such a method?
  • Is there a preferred alternative of getting this functionality?
  • Is it good idea to implement this myself?
Alexis C.
  • 91,686
  • 21
  • 171
  • 177
Hirle
  • 188
  • 6
  • Indeed sort of answers my questions 1) methods isn't there out of the box 2) lots of options, no consensus about what is preferred, custom collector looks nice 3) implementing a custom collector is what people are suggesting. So thanks for the link, searched for it but apparently not good enough. Any action required from me to confirm this as a duplicate? – Hirle Mar 05 '15 at 18:01
  • There's also the answer from Brian Goetz you should consider (either by wrapping it in a simple utility method); or with the Guava Iterators utility class; which have the `Single` behavior you are looking for. `T elem = Iterators.getOnlyElement(stream.iterator());` – Alexis C. Mar 05 '15 at 18:17
  • 1
    `stream.collect(Collectors.toMap(()->"single", Functions.identity()).get("single")` will also throw if there is more than one element… – Holger Mar 05 '15 at 18:32
  • `Stream` now has `findFirst` - returns an `Optional` (instead of signalling no element by throwing). Don't know in which version it was introduced, but it is there in 17. – davidbak Jul 08 '23 at 03:36

1 Answers1

4

Here's one way to get this functionality:

Stream<String> stream = Stream.of ("first");
String single = stream.reduce((a,b)->{throw new SomeException();})
                      .get();

The idea is that if there's more than one element, reduce would throw the exception inside the lambda expression. If there are no elements, get() would throw NoSuchElementException.

You can also change the exception thrown when there are no elements by changing the code to :

Stream<String> stream = Stream.of ("first");
String single = stream.reduce((a,b)->{throw new SomeException();})
                      .orElseThrow(SomeException::new);
Eran
  • 387,369
  • 54
  • 702
  • 768
  • 2
    Why this works is: the [associative](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#Associativity) accumulation function used for the [reduction](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#Reduction) is only called by the stream if there are multiple elements. If there is a single element, the stream resolves to that element. We hijack this feature to throw an exception instead of accumulating the values. It's clever and concise but may confuse, since it's not how the API was intended to be used. – Andrew Spencer Sep 04 '15 at 08:08