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
- Collecting our objects in a
Listwith theCollectors.toList()collector. - Applying an extra finisher at the end, that returns the single element — or throws an
IllegalStateExceptioniflist.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);