Filter Java Stream to 1 and only 1 element

Create a custom Collector

public static <T> Collector<T, ?, T> toSingleton() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                if (list.size() != 1) {
                    throw new IllegalStateException();
                }
                return list.get(0);
            }
    );
}

We use Collectors.collectingAndThen to construct our desired Collector by

  1. Collecting our objects in a List with the Collectors.toList() collector.
  2. Applying an extra finisher at the end, that returns the single element — or throws an IllegalStateException if list.size != 1.

Used as:

User resultUser = users.stream()
        .filter(user -> user.getId() > 0)
        .collect(toSingleton());

You can then customize this Collector as much as you want, for example give the exception as argument in the constructor, tweak it to allow two values, and more.

An alternative — arguably less elegant — solution:

You can use a ‘workaround’ that involves peek() and an AtomicInteger, but really you shouldn’t be using that.

What you could do instead is just collecting it in a List, like this:

LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
List<User> resultUserList = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.toList());
if (resultUserList.size() != 1) {
    throw new IllegalStateException();
}
User resultUser = resultUserList.get(0);

Leave a Comment