JDK-8201273 : add operation to extract the only element from a stream
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.util.stream
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2018-04-06
  • Updated: 2018-11-14
Related Reports
Relates :  
Description
There are cases where the result of a stream should have *exactly* one element. The findAny() operation will get the element, but this loses two ways: 1) it returns an optional which *should* have a value always present, so one has to do a getOrThrow(); 2) if there is more than one element, this fact is lost.

An alternative would be

    List<T> list = stream.<operation>.<operation>. ...
        .limit(2)
        .collect(toList());
    assert list.size() == 1;
    T result = list.get(0);

This is fairly cumbersome.

This function could be expressed as a terminal operation, e.g. stream.findOnly(), or it could be expressed as a collector, e.g. stream.collect(onlyElement()).

Comments
A variation is "at-most-one" element. Guava has this as well, a toOptional() collector. (This might be better as a separate RFE.)
14-11-2018

Guava has the onlyElement() collector. It throws NoSuchElementException if there are zero elements, and it throws IllegalArgumentException of there are two or more elements. Guava also has Iterators.getOnlyElement(), which behaves similarly. Note: it's unnecessary to add anything to Iterator, if there's a convenient way to convert an iterator to a stream, as suggested by JDK-8181054. Adding stream.findOnly() seems well aligned with findAny(), but perhaps only from an implementation perspective. It can short-circuit, but only if there's more than one element in the stream; a collector can't do that. OTOH that seems to be the error case, so optimizing for it doesn't seem all that useful. (Well, the collector can "short-circuit" by throwing an exception.) Also, findOnly() would return a T whereas findFirst() and findAny() return Optional<T>, a bit of asymmetry. See also https://stackoverflow.com/a/22717262/1441122
14-11-2018