22

我读了为什么 Java 的迭代器不是可迭代的?为什么枚举不是可迭代的?,但我仍然不明白为什么会这样:

void foo(Iterator<X> it) {
  for (X x : it) {
    bar(x);
    baz(x);
  }
}

没有成为可能。换句话说,除非我遗漏了什么,否则上面的内容可能是很好且有效的语法糖:

void foo(Iterator<X> it) {
  for (X x; it.hasNext();) {
    x = it.next();
    bar(x);
    baz(x);
  }
}
4

6 回答 6

19

最有可能的原因是迭代器不可重用。每次要迭代元素时,都需要从 Iterable 集合中获取一个新的迭代器。但是,作为快速修复:

private static <T> Iterable<T> iterable(final Iterator<T> it){
     return new Iterable<T>(){ public Iterator<T> iterator(){ return it; } };
}

//....
{
     // ...
     // Now we can use:
     for ( X x : iterable(it) ){
        // do something with x
     }
     // ...
}
//....

也就是说,最好的办法是简单地绕过Iterable<T>界面而不是Iterator<T>

于 2010-04-08T07:37:17.007 回答
10

但我仍然不明白为什么这[...]无法实现。

我可以看到几个原因:

  1. Iterators 不可重用,因此 for/each 会消耗迭代器 - 也许不是不正确的行为,但对于那些不知道 for/each 是如何脱糖的人来说是不直观的。
  2. Iterators 在代码中并不经常出现“赤裸”,因此它会使 JLS 变得复杂而几乎没有收获(for/each 结构已经够糟糕了,同时在Iterables 和数组上工作)。
  3. 有一个简单的解决方法。仅仅为此分配一个新对象似乎有点浪费,但是分配很便宜,而且在大多数情况下,逃逸分析甚至可以免除您的小成本。(不过,为什么他们没有在Iterables实用程序类中包含这种解决方法,类似于Collectionsand Arrays,但我无法理解。)
  4. (可能不是真的 - 见评论。)我似乎记得 JLS 只能引用java.lang[需要引用]中的东西,所以他们必须创建一个Iterator接口,在java.lang其中java.util.Iterator扩展而不添加任何东西。现在我们有两个功能等效的迭代器接口。50% 使用裸迭代器的新代码会选择java.lang版本,其余的使用java.util. 随之而来的是混乱,兼容性问题比比皆是,等等。

我认为第 1-3 点非常符合 Java 语言设计理念的走向:不要让新手感到惊讶,如果规范没有明显的收益超过成本,不要使规范复杂化,并且不要不要用语言特性来做图书馆可以做的事情。

同样的论点也可以解释为什么java.util.Enumeration不是Iterable

于 2010-04-08T10:34:30.033 回答
7

for(Type t : iterable)语法仅对实现Iterable<Type>.

迭代器不实现可迭代。

您可以迭代诸如 , 之类的东西Collection<T>List<T>或者Set<T>因为它们实现了 Iterable。

下面的代码是等价的:

for (Type t: list) {
    // do something with t
}

Iterator<Type> iter = list.iterator();
while (iter.hasNext()) {
    t = iter.next();
    // do something with t
}

之所以无法做到这一点,是因为在语言中添加了 for-each 语法以抽象出Iterator. 使 for-each 循环与迭代器一起工作不会完成创建 for-each 循环的目的。

于 2010-04-08T07:38:10.990 回答
6

事实上,你可以。

java 8 上有很短的解决方法:

for (X item : (Iterable<X>) () -> iterator)

有关技巧的详细说明,请参阅如何在 java 8 流上使用 foreach 循环进行迭代。

并且可以在相关问题中找到一些解释为什么这不是本机支持的:

为什么 Stream<T> 没有实现 Iterable<T>?

于 2015-07-29T15:30:09.867 回答
2

迭代器并不意味着可重用(即:在多个迭代循环中使用)。特别是,Iterator.hasNext()保证您可以安全地调用Iterator.next()并确实从基础集合中获取下一个值。

当在两个并发运行的迭代中使用相同的迭代器时(让我们假设一个多线程场景),这个承诺就不能再被保留:

while(iter.hasNext() {
   // Now a context switch happens, another thread is performing
   //    iter.hasNext(); x = iter.next();

  String s = iter.next();  
          // A runtime exception is thrown because the iterator was 
          // exhausted by the other thread
}

这样的场景完全破坏了 Iterator 提供的协议。实际上,它们甚至可以出现在单线程程序中:一个迭代循环调用另一个方法,该方法使用相同的迭代器来执行它自己的迭代。当此方法返回时,调用者正在发出一个Iterator.next()调用,该调用再次失败。

于 2010-04-08T07:45:27.340 回答
0

因为 for-each 被设计为如下所示:

for each element of [some collection of elements]

Iterator不是[some collection of elements]。_ 一个数组和一个Iterable是。

于 2010-04-08T07:50:32.263 回答