【问题标题】:What is the best way to get the count/length/size of an iterator?获取迭代器的计数/长度/大小的最佳方法是什么?
【发布时间】:2012-03-31 23:43:22
【问题描述】:

是否有一种“计算上”的快速方法来获取迭代器的计数?

int i = 0;
for ( ; some_iterator.hasNext() ; ++i ) some_iterator.next();

... 似乎是在浪费 CPU 周期。

【问题讨论】:

  • 迭代器不一定对应于带有“计数”的东西...
  • 迭代器就是它们的样子;迭代到集合的下一个对象(它可以是集合、数组等任何东西)。为什么当他们不关心他们试图迭代的内容时,他们需要告诉大小? to provide an implementation-independent method for access, in which the user does not need to know whether the underlying implementation is some form of array or of linked list, and allows the user go through the collection without explicit indexing.penguin.ewu.edu/~trolfe/LinkedSort/Iterator.html

标签: java iterator


【解决方案1】:

使用Guava library

int size = Iterators.size(iterator);

在内部它只是迭代所有元素,所以它只是为了方便。

【讨论】:

  • 这很优雅。请记住,您正在使用迭代器(即,迭代器之后将为空)
  • 这不是“计算速度快”,这是一种方便的方法,具有消耗迭代器的不良副作用。
  • 您能解释一下这是如何工作的吗? @Andrejs List> wordCountsWithGroupByKey = wordsPairRdd.groupByKey() .mapValues(intIterable -> Iterables.size(intIterable)).collect(); System.out.println("wordCountsWithGroupByKey: " + wordCountsWithGroupByKey); " Iterables.size(intIterable)?
  • Apache Commons 变体:int size = IteratorUtils.size(iterator)
【解决方案2】:

如果您刚刚获得了迭代器,那么这就是您必须要做的 - 它不知道还有多少项需要迭代,因此您无法查询它为了那个结果。有一些实用方法似乎 可以有效地执行此操作(例如 Guava 中的 Iterators.size()),但在下面,它们只是消耗迭代器并在运行时进行计数,与您的示例相同。

但是,许多迭代器来自集合,您通常可以查询它们的大小。如果它是一个用户创建的类,您正在为其获取迭代器,您可以考虑在该类上提供一个 size() 方法。

简而言之,在您拥有迭代器的情况下,没有更好的方法,但通常情况下,您可以访问底层集合或对象,您可以从中访问直接获取大小。

【讨论】:

  • 注意Iterators.size(...) 的副作用(在下面的其他 cmets 和 java-doc 中提到):“返回迭代器中剩余的元素数。迭代器将被耗尽:它的 hasNext () 方法将返回 false。”这意味着,您以后不能再使用迭代器了。 Lists.newArrayList(some_iterator); 可能会有所帮助。
【解决方案3】:

当您到达迭代器的末尾时,您的代码会给您一个异常。你可以这样做:

int i = 0;
while(iterator.hasNext()) {
    i++;
    iterator.next();
}

如果您有权访问基础集合,则可以致电 coll.size()...

编辑 好的,您已修改...

【讨论】:

  • 效率如何?如果迭代器像一百万个值怎么办?
  • @Micro 从技术上讲,迭代器可能是无限的——在这种情况下,循环将永远持续下去。
【解决方案4】:

您将始终需要迭代。然而,您可以使用 Java 8、9 来进行计数,而无需显式循环:

Iterable<Integer> newIterable = () -> iter;
long count = StreamSupport.stream(newIterable.spliterator(), false).count();

这是一个测试:

public static void main(String[] args) throws IOException {
    Iterator<Integer> iter = Arrays.asList(1, 2, 3, 4, 5).iterator();
    Iterable<Integer> newIterable = () -> iter;
    long count = StreamSupport.stream(newIterable.spliterator(), false).count();
    System.out.println(count);
}

打印出来:

5

有趣的是,您可以通过更改此调用中的 parallel 标志来并行化计数操作:

long count = StreamSupport.stream(newIterable.spliterator(), *true*).count();

【讨论】:

    【解决方案5】:

    使用Guava library,另一种选择是将Iterable 转换为List

    List list = Lists.newArrayList(some_iterator);
    int count = list.size();
    

    如果您还需要在获取迭代器的大小后访问它的元素,请使用它。通过使用Iterators.size(),您将无法再访问迭代的元素。

    【讨论】:

    • @LoveToCode 效率低于原始问题上的示例
    • 当然,创建一个包含所有元素的新对象比迭代和丢弃要慢。恕我直言,这个解决方案是一种提高代码可读性的单行器。我经常将它用于元素很少(最多 1000 个)或速度不是问题的集合。
    【解决方案6】:

    如果你只有迭代器,那么不,没有“更好”的方法。如果迭代器来自一个集合,你可以像大小一样。

    请记住,Iterator 只是一个用于遍历不同值的接口,您很可能拥有这样的代码

        new Iterator<Long>() {
            final Random r = new Random();
            @Override
            public boolean hasNext() {
                return true;
            }
    
            @Override
            public Long next() {
                return r.nextLong();
            }
    
            @Override
            public void remove() {
                throw new IllegalArgumentException("Not implemented");
            }
        };
    

        new Iterator<BigInteger>() {
            BigInteger next = BigInteger.ZERO;
    
            @Override
            public boolean hasNext() {
                return true;
            }
    
            @Override
            public BigInteger next() {
                BigInteger current = next;
                next = next.add(BigInteger.ONE);
                return current;
            }
    
            @Override
            public void remove() {
                throw new IllegalArgumentException("Not implemented");
            }
        }; 
    

    【讨论】:

      【解决方案7】:

      没有更有效的方法,如果你只有迭代器。如果迭代器只能使用一次,那么在获取迭代器的内容之前获取计数是……有问题的。

      解决方案是更改您的应用程序使其不需要计数,或者通过其他方式获取计数。 (例如,传递Collection 而不是Iterator ...)

      【讨论】:

        【解决方案8】:

        对于您可以使用的 Java 8

        public static int getIteratorSize(Iterator iterator){
                AtomicInteger count = new AtomicInteger(0);
                iterator.forEachRemaining(element -> {
                    count.incrementAndGet();
                });
                return count.get();
            }
        

        【讨论】:

        • 为什么是AtomicInteger
        【解决方案9】:

        迭代器对象包含与您的集合相同数量的元素。

        List<E> a =...;
        Iterator<E> i = a.iterator();
        int size = a.size();//Because iterators size is equal to list a's size.
        

        但与其获取迭代器的大小并通过索引 0 迭代到那个大小,不如通过迭代器的方法 next() 进行迭代。

        【讨论】:

        • 如果我们没有a,而只有i怎么办?
        猜你喜欢
        • 1970-01-01
        • 2020-12-23
        • 2012-05-30
        • 1970-01-01
        • 2016-05-05
        • 1970-01-01
        • 1970-01-01
        • 2019-07-24
        • 2012-11-14
        相关资源
        最近更新 更多