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

2022-09-02 00:51:00

我读了为什么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);
  }
}

答案 1

最有可能的原因是因为迭代器不可重用;每次要迭代元素时,都需要从 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>


答案 2

但我仍然不明白为什么这[...]没有成为可能。

我可以看到几个原因:

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

我认为第1-3点非常符合Java语言设计理念:不要让新手感到惊讶,如果它没有明显的收益来掩盖成本,就不要让规范复杂化,并且不要用语言功能来做库可以做的事情。

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