获取Java中Iterable的大小


88

我需要弄清楚IterableJava中元素的数量。我知道我可以这样做:

Iterable values = ...
it = values.iterator();
while (it.hasNext()) {
  it.next();
  sum++;
}

我也可以这样做,因为我不再需要Iterable中的对象:

it = values.iterator();
while (it.hasNext()) {
  it.remove();
  sum++;
}

小型基准测试没有表现出太大的性能差异,对此问题有任何意见或其他想法吗?


为什么?都不是一个好主意,因为两者都是O(N)。您应该避免避免两次迭代集合。
罗恩侯爵

3
您的第二个甚至不起作用。你可以不叫remove()不叫next()事前。
Louis Wasserman

Answers:


122

TL; DR:使用Iterables.size(Iterable)强大的Guava库的实用程序方法。

在您的两个代码段中,应该使用第一个,因为第二个将删除中的所有元素values,因此之后为空。更改简单查询的数据结构(例如其大小)是非常意外的。

为了提高性能,这取决于您的数据结构。例如,如果实际上是an ArrayList,则从头开始删除元素(第二种方法正在做的事情)非常慢(计算大小变为O(n * n)而不是应有的O(n))。

通常,如果有可能values是aCollection而不是a Iterable,请检查并致电size()以防万一:

if (values instanceof Collection<?>) {
  return ((Collection<?>)values).size();
}
// use Iterator here...

要将呼叫size()通常比计算元素的数量快得多,而且这一招正是Iterables.size(Iterable)番石榴为你做。


他说他对这些元素不感兴趣,也不在乎是否将其删除。
aioobe

小澄:它会删除元素values
米克尔

1
但是代码的其他部分可能仍在使用相同的Iterable。如果不是现在,也许将来。
菲利普·温德勒

我建议使Google Guava答案更加突出。即使琐碎,也没有理由让人们再次编写此代码。
史蒂芬·哈里森

8
如果您使用Java 8,则创建一个Stream并在其中计数元素为:Stream.of(myIterable).count()
FrankBr

41

如果您使用的是Java 8,则可以使用:

Iterable values = ...
long size = values.spliterator().getExactSizeIfKnown();

仅当可迭代源的大小确定时,它才起作用。大多数集合拆分器都可以,但是如果来自HashSetResultSet例如,您可能会遇到问题。

您可以在此处检查javadoc。

如果不是Java 8选项,或者如果您不知道迭代的来源,则可以使用与guava相同的方法:

  if (iterable instanceof Collection) {
        return ((Collection<?>) iterable).size();
    } else {
        int count = 0;
        Iterator iterator = iterable.iterator();
        while(iterator.hasNext()) {
            iterator.next();
            count++;
        }
        return count;
    }

1
有人给这个家伙一块勋章。
普拉梅什(Pramesh Bajracharya)

对于Sort来说,它对我不起作用。新蜜蜂的回答对我有用。
Sabir Khan

18

这也许有点晚了,但可能会对某人有所帮助。我Iterable在我的代码库中遇到了类似的问题,解决方案是在for each不显式调用的情况下使用values.iterator();

int size = 0;
for(T value : values) {
   size++;
}

2
对我来说,这是一种直观的方法,我对此表示赞赏。几分钟前刚刚使用过。谢谢。
Matt Cremeens 2015年

2
是的,这很直观,但是很遗憾,您必须禁止使用未使用的警告以提高价值……
Nils-o-mat

感谢此解决方案,它确实返回了对象的大小。唯一的缺点是,如果要稍后再次遍历同一对象,则首先应以某种方式对其进行重置。
Zsolti

7

您可以将可迭代对象转换为列表,然后在其上使用.size()。

Lists.newArrayList(iterable).size();

为了清楚起见,上述方法将需要以下导入:

import com.google.common.collect.Lists;

2
Lists是番石榴课
Paul Jackson

6

严格来说,可迭代没有大小。将数据结构视为一个循环。

考虑下面的Iterable实例,无大小:

    new Iterable(){

        @Override public Iterator iterator() {
            return new Iterator(){

                @Override
                public boolean hasNext() {
                    return isExternalSystemAvailble();
                }

                @Override
                public Object next() {
                    return fetchDataFromExternalSystem();
                }};
        }};

2

it.next()之所以会去,next()是因为它是可以执行的简单原因,而这remove()是一个可选操作。

E next()

返回迭代中的下一个元素。

void remove()

从基础集合中移除迭代器返回的最后一个元素(可选操作)


尽管这个答案在技术上是正确的,但却是极具误导性的。调用remove已被认为是对的元素进行计数的错误方式 Iterator,因此,我们是否要执行的操作是否实现并不重要。
史蒂芬·哈里森

1
答案的哪一部分是误导性的?而在其中的情况下remove 执行,为什么会是错误的使用呢?顺便说一句,不赞成投票通常用于错误的答案或给出不好建议的答案。我看不出这个答案如何符合任何条件。
aioobe

2

Java 8及以上

StreamSupport.stream(data.spliterator(), false).count();

0

对我来说,这些只是不同的方法。第一个将您要迭代的对象保留不变,而第二个则将其保留为空。问题是你想做什么。删除的复杂性基于可迭代对象的实现。如果您使用的是Collections,则只需获得Kazekage Gaara提出的尺寸即可,这通常是最好的进近性能。


-2

为什么不简单地使用size()方法Collection来获取元素数量呢?

Iterator 只是要迭代,别无其他。


4
谁说他在使用收藏?:-)
aioobe

您正在使用迭代器,该迭代器支持删除元素。如果在进入迭代器循环之前选择了大小,则需要小心删除并相应地更新值。
Miquel 2012年

1
为什么他可能没有要查看大小的集合的一个示例:他可能正在调用仅返回Iterable的第三方API方法。
史蒂夫·2012年

2
这个答案混淆IteratorIterable。查看正确答案的投票结果。
史蒂芬·哈里森

-4

无需使用循环并计算每个元素或使用第三方库,我们可以简单地在ArrayList中类型化可迭代对象并获取其大小。

((ArrayList) iterable).size();

3
并非所有可迭代项都可以强制转换为ArrayList tho
Alexander Mills
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.