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

发布于 2024-10-20 13:32:16 字数 351 浏览 3 评论 0原文

我有以下情况:

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

假设我无法更改 getSetFromSomewhere() 的返回类型,是否有比

  • 迭代集合并返回 更好或更正确的方法来返回集合中的唯一元素立即
  • 从集合中创建一个列表并调用 .get(0)

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

变身佩奇 2024-10-27 13:32:16

您可以使用 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;

您通常会将其包装在自己的方法中:

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

    // The code I mentioned above...
}

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

Element element = Iterables.getOnlyElement(set);

如果您对它的实现方式感到好奇,可以查看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());
  }

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());
  }
寂寞陪衬 2024-10-27 13:32:16

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

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

如果已知集合类是SortedSet(例如TreeSet< /code> 或 ConcurrentSkipListSet),那么更好的解决方案是:

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

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


对于 HashSetLinkedHashSet,第一个解决方案在时间和空间上都是 O(1),但对于其他类型的集合来说通常更糟。

第二个时间复杂度为 O(logN),并且 TreeSetConcurrentSkipListSet 不使用空间。

从设置的内容创建列表然后调用 List.get(0) 的方法提供了一个糟糕的解决方案,因为第一步是一个 O(N) 操作,两者在时间和空间上。


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

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.

み青杉依旧 2024-10-27 13:32:16

你可以抓住迭代器:

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

You could grab the iterator:

Element firstEl = set.iterator().next();
最笨的告白 2024-10-27 13:32:16

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

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

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文