Why does Java not allow foreach on iterators (only on iterables)?

Possible Duplicate:
Why is Java's Iterator not an Iterable?

Idiomatic way to use for-each loop given an iterator?

Can we use for-each loop for iterating the objects of Iterator type?

The foreach loop are as far as I know syntax sugar added in Java 5. So

Iterable<O> iterable;
for(O o : iterable) {
// Do something
}

will essentially produce the same bytecode as

Iterable<O> iterable;
for(Iterator<O> iter = iterable.iterator(); iter.hasNext(); /* NOOP */) {
O o = iter.next();
// Do something
}

However, if I do not have an iterable in the first place, but only an iterator (say, because a class offers two different iterators), I cannot use the syntax sugar foreach loop. Obviously I can still do the plain old style iteration. However, I'd actually like to do:

Iterator<O> iter;
for(O o : iter /* Iterator<O>, not Iterable<O>! */) {
// Do something
}

And of course I can do a fake Iterable:

class Adapter<O> implements Iterable<O> {
Iterator<O> iter;


public Adapter(Iterator<O> iter) {
this.iter = iter;
}


@Override
public Iterator<O> iterator() {
return iter;
}
}

(Which in fact is an ugly abuse of the Iterable API, as it can only be iterated once!)

If it were designed around Iterator instead of iterable, one could do a number of interesting things:

for(O o : iterable.iterator()) {} // Iterate over Iterable and Collections


for(O o : list.backwardsIterator()) {} // Or backwards


Iterator<O> iter;
for(O o : iter) {
if (o.something()) { iter.remove(); }
if (o.something()) { break; }
}
for(O : iter) { } // Do something with the remaining elements only.

Does anyone know why the language was designed this way? To avoid ambiguity if a class would implement both Iterator and Iterable? To avoid programmer errors that assume that "for(O o : iter)" will process all elements twice (and forget to get a fresh iterator)? Or is there some other reason for this?

Or is there some language trick I just do not know?

90774 次浏览

尽管 Iterator 接口已经在使用中,但是正如 原版的 JSR中所描述的,Iterable 接口正是为此目的创建的(对于循环增强)。

关于 JSR 中讨论的新接口(注意包名) :

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator(在 JSR 中建议改装到 java.util.Iterator上,但实际上并没有这样做)

JSR 说:

这些新的接口用于防止语言对 java.util的依赖,否则会导致。

所以我现在有一个比较合理的解释:

简而言之: 因为这种语法也适用于 < em > 数组 ,它们没有迭代器。

如果语法按照我的提议围绕 Iterator进行设计,那么它将与数组不一致。让我举三个例子:

A)由 Java 开发人员选择的 :

Object[] array;
for(Object o : array) { }
Iterable<Object> list;
for(Object o : list) { }
Iterator<Object> iter;
while(iter.hasNext()) { Object o = iter.next(); }

它的行为方式相同,在数组和集合之间是 非常一致。 然而,迭代器必须使用经典的迭代风格(这至少不太可能导致错误)。

B) allow 数组和 Iterators:

Object[] array;
for(Object o : array) { }
Iterable<Object> list;
for(Object o : list.iterator()) { }
Iterator<Object> iter;
for(Object o : iter) { }

现在数组和集合是不一致的; 但是数组和 ArrayList 是非常密切相关的,应该以相同的方式运行。现在,如果在任何时候,语言是 延期,使例如数组实现 Iterable,它就变得不一致。

C)允许这三种情况同时发生:

Object[] array;
for(Object o : array) { }
Iterable<Object> list;
for(Object o : list) { }
Iterator<Object> iter;
for(Object o : iter) { }

Now if we end up in unclear situations when either someone implements 都有 Iterable and Iterator (is the for loop supposed to get a new iterator or iterate over the current - happens easily in tree-like structures!?!). A simple tie-braker ala "Iterable beats Iterator" unfortunately won't do: it suddenly introduces runtime vs. compile time difference and generics issues.

现在,我们突然需要注意是否要在集合/可迭代文件或数组上进行迭代,此时,我们只获得了很少的好处,代价是造成了很大的混淆。

在 Java (A)中“ for each”的方式是非常一致的,它导致很少的编程错误,并且它允许将来可能发生的将数组转换为常规对象的变化。

有一种变体 D)也可以正常工作: For-each 仅用于迭代器。最好是通过向基元数组添加一个 .iterator()方法:

Object[] array;
for(Object o : array.iterator()) { }
Iterable<Object> list;
for(Object o : list.iterator()) { }
Iterator<Object> iter;
for(Object o : iter) { }

但这需要更改执行期函式库,而不仅仅是编译器,并且破坏了向后兼容性。另外,上面提到的混淆仍然存在

Iterator<Object> iter;
for(Object o : iter) { }
for(Object o : iter) { }

只在数据上迭代一次。

因为“ for”循环会破坏迭代器。不能重置迭代器(即。除非它实现 ListIterator 子接口。

一旦您将迭代器放入一个“ for”循环,它将不再可用。我的猜测是,语言设计人员认为,结合编译器中额外的特殊情况(其中已经有两个 Iterable 和数组)将其转换为字节码(您不能重用与 Iterable 相同的转换) ,这足以成为一个不实现它的批评者。

当您自己通过迭代器接口在代码中完成这项工作时,至少可以清楚地看到发生了什么。

随着 lambdas 的到来,他们可以轻松地做到这一点:

Iterator<String> iterator = ...;
Collections.each ( iterator, (String s) => { System.out.println(s); } );


List<String> list = ...;
Collections.each ( list, (String s) => { System.out.println(s); } );

没有破坏向下兼容,语法也相对简单。我怀疑他们是否会在不同的接口中构建诸如“ each”、“ Collection”和“ map”之类的方法,因为这会破坏向后兼容性,而且还有数组要处理。

我认为答案的一部分可能隐藏在 for-each 循环是语法 Sugar 这一事实中。重点是你想让人们做的事情变得容易很多。还有(至少在我的经验中)这个习语

Iterator iterator = iterable.iterator();
while( iterator.hasNext() ) {
Element e = (Element)iterator.next() ;
}

在旧式代码中经常发生,而使用多个迭代器做一些奇特的事情却没有发生。