Java:为什么不能遍历迭代器?

时间:2010-04-08 07:29:40

标签: java iterator iterable

我看过Why is Java's Iterator not an Iterable?Why aren't Enumerations Iterable?,但我仍然不明白为什么会这样:

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);
  }
}

6 个答案:

答案 0 :(得分:19)

最有可能的原因是因为迭代器不可重复使用;每次想要迭代元素时,你需要从Iterable集合中获得一个新的Iterator。但是,作为一个快速解决方案:

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>

答案 1 :(得分:9)

  

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

我可以看到几个原因:

  1. Iterator s不可重复使用,因此for / each会消耗迭代器 - 也许不是不正确的行为,但对那些不知道for / each如何被淘汰的人来说是不直观的。
  2. Iterator s在代码中看起来并不是“赤裸裸”,因此它会使JLS变得很复杂,而且收益很小(for / each结构很糟糕,同时处理{{1} s和数组)。
  3. 有一个easy workaround。为此分配一个新对象可能看起来有点浪费,但分配很便宜,并且逃逸分析在大多数情况下甚至可以消除那么小的成本。 (为什么他们没有在Iterable实用程序类中包含此变通方法,类似于IterablesCollections,但是我已经超出了我的范围。)
  4. (可能不是真的 - 请参阅评论。)我似乎记得JLS只能引用Arrays [citation needed] 中的内容,所以他们有在java.lang中创建Iterator接口java.lang扩展而不添加任何内容。现在我们有两个功能相当的迭代器接口。使用裸迭代器的新代码的50%将选择java.util.Iterator版本,其余使用java.lang中的版本。随之而来的是混乱,兼容性问题比比皆是等等。
  5. 我认为第1-3点与Java语言设计理念似乎非常一致:不要让新手感到惊讶,如果没有明显的收益来掩盖成本,请不要使规范复杂化,并且不使用语言功能可以使用库来完成。

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

答案 2 :(得分:6)

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循环的创建。

答案 3 :(得分:4)

实际上,你可以。

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

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

有关技巧的详细说明,请参阅How to iterate with foreach loop over java 8 stream

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

Why does Stream<T> not implement Iterable<T>?

答案 4 :(得分: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()调用,该调用再次失败。

答案 5 :(得分:0)

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

for each element of [some collection of elements]

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