The correct way to return the only element from a set

JavaCollectionsSet

Java Problem Overview


I have the following kind of situation:

Set<Element> set = getSetFromSomewhere();
if (set.size() == 1) {
    // return the only element
} else {
    throw new Exception("Something is not right..");
}

Assuming I cannot change the return type of getSetFromSomewhere(), is there a better or more correct way to return the only element in the set than

  • Iterating over the set and returning immediately
  • Creating a list from the set and calling .get(0)

Java Solutions


Solution 1 - Java

You can use an Iterator to both obtain the only element as well as verify that the collection only contains one element (thereby avoiding the size() call and the unnecessary list creation):

Iterator<Element> iterator = set.iterator();

if (!iterator.hasNext()) {
    throw new RuntimeException("Collection is empty");
}

Element element = iterator.next();

if (iterator.hasNext()) {
    throw new RuntimeException("Collection contains more than one item");
}

return element;

You would typically wrap this up in its own method:

public static <E> E getOnlyElement(Iterable<E> iterable) {
    Iterator<E> iterator = iterable.iterator();

    // The code I mentioned above...
}

Note that this implementation is already part of Google's Guava libraries (which I highly recommend, even if you don't use it for this particular code). More specifically, the method belongs to the Iterables class:

Element element = Iterables.getOnlyElement(set);

If you're curious about how it is implemented, you can look at the Iterators class source code (the methods in Iterables often call methods in Iterators):

  /**
   * Returns the single element contained in {@code iterator}.
   *
   * @throws NoSuchElementException if the iterator is empty
   * @throws IllegalArgumentException if the iterator contains multiple
   *     elements.  The state of the iterator is unspecified.
   */
  public static <T> T getOnlyElement(Iterator<T> iterator) {
    T first = iterator.next();
    if (!iterator.hasNext()) {
      return first;
    }

    StringBuilder sb = new StringBuilder();
    sb.append("expected one element but was: <" + first);
    for (int i = 0; i < 4 && iterator.hasNext(); i++) {
      sb.append(", " + iterator.next());
    }
    if (iterator.hasNext()) {
      sb.append(", ...");
    }
    sb.append('>');

    throw new IllegalArgumentException(sb.toString());
  }

Solution 2 - Java

The best general solution (where you don't know the actual set class) is:

Element first = set.iterator().next();

If the set class is known to be a SortedSet (e.g. a TreeSet or ConcurrentSkipListSet), then a better solution is:

Element first = ((SortedSet) set).first();

In both cases, an exception will be thrown if the set is empty; check the javadocs. The exception can be avoided using Collection.isEmpty().


The first solution is O(1) in time and space for a HashSet or LinkedHashSet, but typically worse for other kinds of set.

The second one is O(logN) in time , and uses no space for TreeSet or ConcurrentSkipListSet.

The approach of creating a list from the set contents and then calling List.get(0) gives a poor solution since the first step is an O(N) operation, both in time and space.


I failed to notice that N is actually 1. But even so, creating an iterator is likely to be less expensive than creating a temporary list.

Solution 3 - Java

You could grab the iterator:

Element firstEl = set.iterator().next();

Solution 4 - Java

In Java 8, we can do like below:

set.stream().findFirst().get()

But, remember to check the size of set before as Optional.get() throws NoSuchElementException

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJanneView Question on Stackoverflow
Solution 1 - JavaAdam PaynterView Answer on Stackoverflow
Solution 2 - JavaStephen CView Answer on Stackoverflow
Solution 3 - JavaMark ElliotView Answer on Stackoverflow
Solution 4 - JavaLiquidpieView Answer on Stackoverflow