从集合中返回唯一元素的正确方法

Jan*_*nne 43 java collections set

我有以下情况:

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

假设我无法更改返回类型getSetFromSomewhere(),是否有更好或更正确的方法来返回集合中的唯一元素

  • 迭代集合并立即返回
  • 从集合和调用创建列表 .get(0)

Ada*_*ter 50

您既可以使用an Iterator来获取唯一元素,也可以验证集合只包含一个元素(从而避免size()调用和不必要的列表创建):

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;
Run Code Online (Sandbox Code Playgroud)

您通常会使用自己的方法将其包装起来:

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

    // The code I mentioned above...
}
Run Code Online (Sandbox Code Playgroud)

请注意,此实现已经是Google的Guava库的一部分(我强烈建议,即使您不将它用于此特定代码).更具体地说,该方法属于Iterables:

Element element = Iterables.getOnlyElement(set);
Run Code Online (Sandbox Code Playgroud)

如果您对如何实现它感到好奇,可以查看Iterators类源代码(Iterables通常调用方法中的方法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());
  }
Run Code Online (Sandbox Code Playgroud)

  • 在你的情况下,你特别想要`Iterables.getOnlyElement()`.它是番石榴中最受欢迎的方法之一.我强烈推荐它.:-) (4认同)

Ste*_*n C 17

最好的通用解决方案(您不知道实际的集合类)是:

Element first = set.iterator().next();
Run Code Online (Sandbox Code Playgroud)

如果已知集合类是SortedSet(例如a TreeSetConcurrentSkipListSet),那么更好的解决方案是:

Element first = ((SortedSet) set).first();
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,如果集合为空,则抛出异常; 检查javadocs.使用可以避免例外Collection.isEmpty().


第一种解决方案是O(1)时间和空间用于HashSet或者LinkedHashSet,但通常对于其他类型的集合来说更糟糕.

第二个是O(logN)及时的,没有空间用于TreeSetConcurrentSkipListSet.

从设置内容创建列表然后调用的方法List.get(0)给出了一个很差的解决方案,因为第一步是O(N)时间和空间的操作.


我没注意到N实际上是这样1.但即便如此,创建迭代器可能比创建临时列表更便宜.


Mar*_*iot 9

你可以抓住迭代器:

Element firstEl = set.iterator().next();
Run Code Online (Sandbox Code Playgroud)


Liq*_*pie 8

在Java 8中,我们可以这样做:

set.stream().findFirst().get()
Run Code Online (Sandbox Code Playgroud)

Optional.get()但是,请记住在抛出之前检查集合的大小NoSuchElementException