346

I am trying to use Java 8 Streams to find elements in a LinkedList. I want to guarantee, however, that there is one and only one match to the filter criteria.

Take this code:

public static void main(String[] args) { LinkedList<User> users = new LinkedList<>(); users.add(new User(1, "User1")); users.add(new User(2, "User2")); users.add(new User(3, "User3")); User match = users.stream().filter((user) -> user.getId() == 1).findAny().get(); System.out.println(match.toString()); } 

static class User { @Override public String toString() { return id + " - " + username; } int id; String username; public User() { } public User(int id, String username) { this.id = id; this.username = username; } public void setUsername(String username) { this.username = username; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public int getId() { return id; } } 

This code finds a User based on their ID. But there are no guarantees how many Users matched the filter.

Changing the filter line to:

User match = users.stream().filter((user) -> user.getId() < 0).findAny().get(); 

Will throw a NoSuchElementException (good!)

I would like it to throw an error if there are multiple matches, though. Is there a way to do this?

8
  • 1
    count() is a terminal operation so you can't do that. The stream can't be used after. Commented Mar 27, 2014 at 17:42
  • Ok, thanks @ZouZou. I wasn't entirely certain what that method did. Why is there no Stream::size ? Commented Mar 27, 2014 at 17:44
  • 11
    @ryvantage Because a stream can only be used once: calculating its size means "iterating" over it and after that you can't use the stream any longer. Commented Mar 27, 2014 at 17:45
  • 5
    Wow. That one comment helped me understand Streams so much more than I did before... Commented Mar 27, 2014 at 17:50
  • 4
    This is when you realize that you had needed to use a LinkedHashSet (assuming you want insertion order preserved) or a HashSet all along. If your collection is only used to find a single user id, then why are you collecting all the other items? If there is a potential that you will always need to find some user id which also needs to be unique, then why use a list and not a set? You are programming backwards. Use the right collection for the job and save yourself this headache Commented Nov 28, 2017 at 16:22

26 Answers 26

307
+200

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); 
Sign up to request clarification or add additional context in comments.

21 Comments

Guava's Iterables.getOnlyElement would shorten these solutions and provide better error messages. Just as a tip for fellow readers who already use Google Guava.
i wrapped this idea up into a class - gist.github.com/denov/a7eac36a3cda041f8afeabcef09d16fc
The custom collector still collects all the items, which is O(n), isn't there a way to shortcut it? Get a single item can be done in 1 step, checking if another one exists is also 1 step, no matter how many more items are in the filtered stream.
@skiwi: Lonely's edit was helpful and correct, so I re-instated it after review. People visiting this answer today don't care about how you came to the answer, they don't need to see the old version and new version and an Updated section. That makes your answer more confusing and less helpful. It is much better to put posts in a final state, and if people want to see how it all played out they can view the post history.
I have to say that I really don't like the name toSingleton, since that is misleading. It is not a singleton it returns, which I consider to be a reserved word in programming. This is a 'single element', or 'one instance'.
|
188

For the sake of completeness, here is the ‘one-liner’ corresponding to @prunge’s excellent answer:

User user1 = users.stream() .filter(user -> user.getId() == 1) .reduce((a, b) -> { throw new IllegalStateException("Multiple elements: " + a + ", " + b); }) .get(); 

This obtains the sole matching element from the stream, throwing

  • NoSuchElementException in case the stream is empty, or
  • IllegalStateException in case the stream contains more than one matching element.

A variation of this approach avoids throwing an exception early and instead represents the result as an Optional containing either the sole element, or nothing (empty) if there are zero or multiple elements:

Optional<User> user1 = users.stream() .filter(user -> user.getId() == 1) .collect(Collectors.reducing((a, b) -> null)); 

9 Comments

I like the initial approach in this answer. For customization purposes, it is possible to convert the last get() to orElseThrow()
I like the brevity of this one, and the fact that it avoid creating an un-necessary List instance each time it is called.
In case your use case allows for the stream to be empty, omit the .get() on the end of the chain and then you'll get an Optional which will either be empty in case the stream is empty or will be populated with the single element.
I don't think its a good solution since in an error message we will have only first two elements which are invalid and we will not include value of more than two of them.
Caution: Stream#reduce(...) does behave differently than Stream#collect(Collectors.reducing(...)): Both make the stream return an Optional, however Stream#reduce will throw an NPE nevertheless when returning null inside the binary operator. So the code in the answer works, but it wont if you mix up the two code pieces
|
99

Guava provides MoreCollectors.onlyElement() which does the right thing here. But if you have to do it yourself, you could roll your own Collector for this:

<E> Collector<E, ?, Optional<E>> getOnly() { return Collector.of( AtomicReference::new, (ref, e) -> { if (!ref.compareAndSet(null, e)) { throw new IllegalArgumentException("Multiple values"); } }, (ref1, ref2) -> { if (ref1.get() == null) { return ref2; } else if (ref2.get() != null) { throw new IllegalArgumentException("Multiple values"); } else { return ref1; } }, ref -> Optional.ofNullable(ref.get()), Collector.Characteristics.UNORDERED); } 

...or using your own Holder type instead of AtomicReference. You can reuse that Collector as much as you like.

9 Comments

@skiwi's singletonCollector was smaller and easier to follow than this, that's why I gave him the check. But good to see consensus in the answer: a custom Collector was the way to go.
Fair enough. I was primarily aiming for speed, not conciseness.
Yeah? Why is yours faster?
Mostly because allocating an all-up List is more expensive than a single mutable reference.
@LouisWasserman, the final update sentence about MoreCollectors.onlyElement() should actually be first (and perhaps the only :) )
|
96

The other answers that involve writing a custom Collector are probably more efficient (such as Louis Wasserman's, +1), but if you want brevity, I'd suggest the following:

List<User> result = users.stream() .filter(user -> user.getId() == 1) .limit(2) .collect(Collectors.toList()); 

Then verify the size of the result list.

if (result.size() != 1) { throw new IllegalStateException("Expected exactly one user but got " + result); } User user = result.get(0); 

6 Comments

What's the point of limit(2) in this solution? What difference would it make whether the resulting list was 2 or 100? If it's greater than 1.
It stops immediately if it finds a second match. This is what all the fancy collectors do, just using more code. :-)
How about adding Collectors.collectingAndThen(toList(), l -> { if (l.size() == 1) return l.get(0); throw new RuntimeException(); })
Javadoc says this about limit's param: maxSize: the number of elements the stream should be limited to. So, shouldn't it be .limit(1) instead of .limit(2) ?
@alexbt The problem statement is to ensure that there is exactly one (no more, no fewer) matching element. After my code, one can test result.size() to make sure it equals 1. If it's 2, then there's more than one match, so it's an error. If the code instead did limit(1), more than one match would result in a single element, which can't be distinguished from there being exactly one match. This would miss an error case the OP was concerned about.
|
84

Use Guava's MoreCollectors.onlyElement() (source code).

A collector that takes a stream containing exactly one element and returns that element. The returned collector throws an IllegalArgumentException if the stream consists of two or more elements, and a NoSuchElementException if the stream is empty.

This does what you want, throwing an exception if the stream does not contain exactly one element.

Usage:

import static com.google.common.collect.MoreCollectors.onlyElement; User match = users.stream().filter((user) -> user.getId() < 0).collect(onlyElement()); 

1 Comment

Note for other users: MoreCollectors is part of the yet unreleased (as of 2016-12) unreleased version 21.
35

The "escape hatch" operation that lets you do weird things that are not otherwise supported by streams is to ask for an Iterator:

Iterator<T> it = users.stream().filter((user) -> user.getId() < 0).iterator(); if (!it.hasNext()) { throw new NoSuchElementException(); } else { result = it.next(); if (it.hasNext()) { throw new TooManyElementsException(); } } 

Guava has a convenience method to take an Iterator and get the only element, throwing if there are zero or multiple elements, which could replace the bottom n-1 lines here.

1 Comment

Guava's method: Iterators.getOnlyElement(Iterator<T> iterator).
27

Update

Nice suggestion in comment from @Holger:

Optional<User> match = users.stream() .filter((user) -> user.getId() > 1) .reduce((u, v) -> { throw new IllegalStateException("More than one ID found") }); 

Original answer

The exception is thrown by Optional#get, but if you have more than one element that won't help. You could collect the users in a collection that only accepts one item, for example:

User match = users.stream().filter((user) -> user.getId() > 1) .collect(toCollection(() -> new ArrayBlockingQueue<User>(1))) .poll(); 

which throws a java.lang.IllegalStateException: Queue full, but that feels too hacky.

Or you could use a reduction combined with an optional:

User match = Optional.ofNullable(users.stream().filter((user) -> user.getId() > 1) .reduce(null, (u, v) -> { if (u != null && v != null) throw new IllegalStateException("More than one ID found"); else return u == null ? v : u; })).get(); 

The reduction essentially returns:

  • null if no user is found
  • the user if only one is found
  • throws an exception if more than one is found

The result is then wrapped in an optional.

But the simplest solution would probably be to just collect to a collection, check that its size is 1 and get the only element.

3 Comments

I would add an identity element (null) to prevent using get(). Sadly your reduce is not working as you think it does, consider a Stream that has null elements in it, maybe you think that you covered it, but I can be [User#1, null, User#2, null, User#3], now it will not throw an exception I think, unless I'm mistaken here.
@Skiwi if there are null elements the filter will throw a NPE first.
Since you know that the stream can’t pass null to the reduction function, removing the identity value argument would render the entire dealing with null in the function obsolete: reduce( (u,v) -> { throw new IllegalStateException("More than one ID found"); } ) does the job and even better, it already returns an Optional, eliding the necessity for calling Optional.ofNullable on the result.
23

Using reduce

This is the simpler and flexible way I found (based on @prunge answer)

Optional<User> user = users.stream() .filter(user -> user.getId() == 1) .reduce((a, b) -> { throw new IllegalStateException("Multiple elements: " + a + ", " + b); }) 

This way you obtain:

  • the Optional - as always with your object or Optional.empty() if not present
  • the Exception (with eventually YOUR custom type/message) if there's more than one element

2 Comments

This is clearly the most elegant of the solutions on this page.
@LordOfThePigs Thanks, given that there's also an answer from Brian Goetz on this page, it means really a lot ;-)
21

I think this way is more simple:

User resultUser = users.stream() .filter(user -> user.getId() > 0) .findFirst().get(); 

4 Comments

It find only first but the case was also to throw Exception when it is more than one
This is bad practice. It leads to nondeterministic behavior if there are 2 or more objects. Whole JDK's findFirst is bad idea.
@RadekPostołowicz 1) Isn't it only nondeterministic if it's a parallel stream? 2) If there are multiple items that fit the filter, how often do you care which one it is? (Calling it 'first' was probably the bad idea) 3) None of that applies to this post, because of what lczapski said.
It's nondeterministic because it selects first element while there might be many. Without explicit sorting, it's just randomly picking first one.
18

An alternative is to use reduction: (this example uses strings but could easily apply to any object type including User)

List<String> list = ImmutableList.of("one", "two", "three", "four", "five", "two"); String match = list.stream().filter("two"::equals).reduce(thereCanBeOnlyOne()).get(); //throws NoSuchElementException if there are no matching elements - "zero" //throws RuntimeException if duplicates are found - "two" //otherwise returns the match - "one" ... //Reduction operator that throws RuntimeException if there are duplicates private static <T> BinaryOperator<T> thereCanBeOnlyOne() { return (a, b) -> {throw new RuntimeException("Duplicate elements found: " + a + " and " + b);}; } 

So for the case with User you would have:

User match = users.stream().filter((user) -> user.getId() < 0).reduce(thereCanBeOnlyOne()).get(); 

Comments

12

Guava has a Collector for this called MoreCollectors.onlyElement().

Comments

11

Using a Collector:

public static <T> Collector<T, ?, Optional<T>> singleElementCollector() { return Collectors.collectingAndThen( Collectors.toList(), list -> list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty() ); } 

Usage:

Optional<User> result = users.stream() .filter((user) -> user.getId() < 0) .collect(singleElementCollector()); 

We return an Optional, since we usually can't assume the Collection to contain exactly one element. If you already know this is the case, call:

User user = result.orElseThrow(); 

This puts the burden of handeling the error on the caller - as it should.

Comments

7

Using Reduce and Optional

From Fabio Bonfante response:

public <T> T getOneExample(Collection<T> collection) { return collection.stream() .filter(x -> /* do some filter */) .reduce((x,y)-> {throw new IllegalStateException("multiple");}) .orElseThrow(() -> new NoSuchElementException("none")); } 

Comments

2

We can use RxJava (very powerful reactive extension library)

LinkedList<User> users = new LinkedList<>(); users.add(new User(1, "User1")); users.add(new User(2, "User2")); users.add(new User(3, "User3")); User userFound = Observable.from(users) .filter((user) -> user.getId() == 1) .single().toBlocking().first(); 

The single operator throws an exception if no user or more then one user is found.

1 Comment

Correct answer, initialializing a blocking stream or collection is probably not very cheap (in terms of resources) though.
2

As Collectors.toMap(keyMapper, valueMapper) uses a throwing merger to handle multiple entries with the same key it is easy:

List<User> users = new LinkedList<>(); users.add(new User(1, "User1")); users.add(new User(2, "User2")); users.add(new User(3, "User3")); int id = 1; User match = Optional.ofNullable(users.stream() .filter(user -> user.getId() == id) .collect(Collectors.toMap(User::getId, Function.identity())) .get(id)).get(); 

You will get a IllegalStateException for duplicate keys. But at the end I am not sure if the code would not be even more readable using an if.

1 Comment

Fine solution! And if you do .collect(Collectors.toMap(user -> "", Function.identity())).get(""), you have a more generic behaviour.
2

Starting in JDK 24 (available as a preview in JDK 22 and 23), Stream Gatherers can be used to limit a stream to one and only one element as follows.

A state object tracks the element, if any:

class State<T> { private T element; public boolean accept(T element) { if (this.element != null) { throw new IllegalArgumentException("too many elements"); } this.element = element; return true; } public T element() { if (element == null) { throw new NoSuchElementException("no elements"); } return element; } } 

And a gatherer uses the state to get the only object, if there is one, and pass it downstream, or throw an exception if either a second element is seen or if the end of the stream is reached without any element being seen.

<T> Gatherer<T, State<T>, T> onlyOne() { return Gatherer.ofSequential(State::new, Gatherer.Integrator.ofGreedy((state, element, _) -> { return state.accept(element); }), (state, downstream) -> { downstream.push(state.element()); }); } 

When used like:

users.stream().filter(user -> user.getId() == 1).gather(onlyOne()).findFirst().get() 

It will return the matching user, if there is one, or throw IllegalArgumentException or NoSuchElementException if there are no elements. I chose these to match Guava's MoreCollectors::onlyElement but you can change them to suit your needs.

It is efficient in space and time because it throws as soon as it sees a single duplicate element rather than waiting for all the elements, and it only stores a single reference to the first element seen, unlike solutions that accumulate an entire list.

It does not produce the matching element to the downstream as soon as it sees it but rather produces it in the finisher, so if there is going to be a duplicate element found, by the time it sees it and throws, it will not have already produced the first, matching element. Thus in the case where there are duplicates, you don't get the element and later, the exception. You just get the exception.

Comments

1

I am using those two collectors:

public static <T> Collector<T, ?, Optional<T>> zeroOrOne() { return Collectors.reducing((a, b) -> { throw new IllegalStateException("More than one value was returned"); }); } public static <T> Collector<T, ?, T> onlyOne() { return Collectors.collectingAndThen(zeroOrOne(), Optional::get); } 

2 Comments

Neat! onlyOne() throws IllegalStateException for >1 elements, and NoSuchElementException` (in Optional::get) for 0 elements.
@simon04 You could overload the methods to take a Supplier of (Runtime)Exception.
1

If you don't mind using a 3rd party library, SequenceM from cyclops-streams (and LazyFutureStream from simple-react) both a have single & singleOptional operators.

singleOptional() throws an exception if there are 0 or more than 1 elements in the Stream, otherwise it returns the single value.

String result = SequenceM.of("x") .single(); SequenceM.of().single(); // NoSuchElementException SequenceM.of(1, 2, 3).single(); // NoSuchElementException String result = LazyFutureStream.fromStream(Stream.of("x")) .single(); 

singleOptional() returns Optional.empty() if there are no values or more than one value in the Stream.

Optional<String> result = SequenceM.fromStream(Stream.of("x")) .singleOptional(); //Optional["x"] Optional<String> result = SequenceM.of().singleOptional(); // Optional.empty Optional<String> result = SequenceM.of(1, 2, 3).singleOptional(); // Optional.empty 

Disclosure - I am the author of both libraries.

Comments

1
 List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); Integer value = list.stream().filter((x->x.intValue()==8)).findFirst().orElse(null); 

I have used Integer type instead of primitive as it will have null pointer exception. you just have to handle this exception... looks succinct, I think ;)

Comments

0

If you don't use Guava or Kotlin, here's a solution based on @skiwi and @Neuron answers.

users.stream().collect(single(user -> user.getId() == 1)); 

or

users.stream().collect(optional(user -> user.getId() == 1)); 

where single and optional are statically imported functions returning corresponding collectors.

I reasoned it would look more succinct if the filtering logic had been moved inside the collector. Also nothing would break in the code if you happened to delete the string with .filter.

The gist for the code https://gist.github.com/overpas/ccc39b75f17a1c65682c071045c1a079

Comments

0

Tried a sample code for my self and here is the solution for that.

User user = Stream.of(new User(2), new User(2), new User(1), new User(2)) .filter(u -> u.getAge() == 2).findFirst().get(); 

and the user class

class User { private int age; public User(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } 

1 Comment

This doesn't even work. It returns the duplicate user instead of throwing an exception.
0
public List<state> getAllActiveState() { List<Master> master = masterRepository.getActiveExamMasters(); Master activeMaster = new Master(); try { activeMaster = master.stream().filter(status -> status.getStatus() == true).reduce((u, v) -> { throw new IllegalStateException(); }).get(); return stateRepository.getAllStateActiveId(activeMaster.getId()); } catch (IllegalStateException e) { logger.info(":More than one status found TRUE in Master"); return null; } } 
  1. In this above code, As per the condition if its find more than one true in the list then it will through the exception.
  2. When it through the error will showing custom message because it easy maintain the logs on server side.
  3. From Nth number of element present in list just want only one element have true condition if in list there are more than one elements having true status at that moment it will through an exception.
  4. after getting all the this we using get(); to taking that one element from list and stored it into another object.
  5. If you want you added optional like Optional<activeMaster > = master.stream().filter(status -> status.getStatus() == true).reduce((u, v) -> {throw new IllegalStateException();}).get();

Comments

0

Here’s another version in which I fixed some minor issues.

Advantages:

  • Does not scan the entire stream if it contains more than one element (early termination).
  • Does not ignore null values in the stream; they are properly accounted for.
  • Subjectively good readability.
  • Works correctly with parallelStream(), ensuring proper merging of results.

Usage:

Optional<String> single = Stream.of("a").collect(toSingle()); 

Code:

public class SingleUtils { public static <T> Collector<T, ?, Optional<T>> toSingle() { return Collector.of( // Container: { value, counter } () -> new Container<T>(), // Add element (container, element) -> { int count = container.count; if (count > 0) { throw new IllegalStateException("Stream contains more than one element"); } container.value = element; container.count++; }, // Combine parts (used by parallelStream()) (left, right) -> { int leftCount = left.count; int rightCount = right.count; if (leftCount + rightCount > 1) { throw new IllegalStateException("Stream contains more than one element"); } return leftCount > 0 ? left : right; }, // Final mapping container -> Optional.ofNullable(container.value) ); } static class Container<T> { T value; int count; } } 

Unit Tests:

@Test void toSingle_readsStreamOnlyOnce() { AtomicInteger counter = new AtomicInteger(42); IntStream stream = IntStream.generate(counter::getAndIncrement); // Will generate an "unlimited" sequence 42, 43, 44, ... MAX_INT - 1 assertThrows(IllegalStateException.class, () -> stream.mapToObj(Integer.class::cast).collect(SingleUtils.toSingle())); // First time take 42 and increment to 43 // Second time take 43 and increment to 44, then throw exception assertEquals(44, counter.get()); } @Test void toSingle_mixedNullAndValueStream_throwsIllegalStateException() { List<Integer> mixedList = new ArrayList<>(); mixedList.add(1); mixedList.add(null); assertThrows(IllegalStateException.class, () -> mixedList.stream().collect(toSingle())); } @Test void toSingle_parallelStream_singleElement_returnsOptionalWithValue() { List<Integer> list = List.of(42); Optional<Integer> result = list.parallelStream().collect(toSingle()); assertTrue(result.isPresent()); assertEquals(42, result.get()); } @Test void toSingle_parallelStream_multipleElements_throwsIllegalStateException() { List<Integer> list = List.of(4, 2); assertThrows(IllegalStateException.class, () -> list.parallelStream().collect(toSingle())); } 

Comments

-1
User match = users.stream().filter((user) -> user.getId()== 1).findAny().orElseThrow(()-> new IllegalArgumentException()); 

2 Comments

While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.
This doesn't even work. It returns a duplicate user instead of throwing an exception.
-1

Inspired by @skiwi, I solved it the following way:

public static <T> T toSingleton(Stream<T> stream) { List<T> list = stream.limit(1).collect(Collectors.toList()); if (list.isEmpty()) { return null; } else { return list.get(0); } } 

And then:

User user = toSingleton(users.stream().filter(...).map(...)); 

4 Comments

This solution does not detect the case where there are multiple values in the stream. So it goes unnoticed.
Actually, I only wanted to get the first element in the stream.
The original question wanted the one and only one. The accepted answer throws an exception instead.
Yeah... If you want to do exactly the same, you can just do stream.findFirst().orElse(null) which is completely equivalent and a lot more readable that what you are doing here.
-3

Have you tried this

long c = users.stream().filter((user) -> user.getId() == 1).count(); if(c > 1){ throw new IllegalStateException(); } 

long count() Returns the count of elements in this stream. This is a special case of a reduction and is equivalent to: return mapToLong(e -> 1L).sum(); This is a terminal operation. 

Source: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

2 Comments

It was said that count() is not good to use because it is a terminal operation.
If this really is a quote, please add your sources

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.