为什么 Java 不允许在迭代器上使用 foreach(仅在可迭代对象上)?

可能重复:
为什么Java的Iterator不是Iterable?

给定for-each循环的惯用方式迭代器?

我们可以使用用于迭代Iterator类型对象的for-each循环?

据我所知,foreach 循环是 Java 5 中添加的语法糖.所以

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
}

基本上会产生与

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

但是,如果我一开始没有可迭代对象,而只有一个迭代器(比如说,因为一个类提供了两个不同的迭代器),我就不能使用语法糖 foreach 循环.显然我仍然可以进行简单的旧样式迭代.但是,我实际上想做:

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
}

当然我可以做一个假的Iterable:

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

(这实际上是对 Iterable API 的丑陋滥用,因为它只能迭代一次!)

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

如果它是围绕 Iterator 而不是 iterable 设计的,可以做很多有趣的事情:

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.

有人知道为什么语言是这样设计的吗?如果一个类同时实现 IteratorIterable,为了避免歧义?为了避免假定for(O o : iter)"的程序员错误;将处理所有元素两次(并忘记获取新的迭代器)?还是有其他原因?

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?

推荐答案

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

So I have a somewhat reasonable explanation now:

短版:因为语法也适用于没有迭代器的数组.

Short version: Because the syntax also applies to arrays, which don't have iterators.

如果语法是按照我的建议围绕 Iterator 设计的,它将与数组不一致.让我给出三个变体:

If the syntax were designed around Iterator as I proposed, it would be inconsistent with arrays. Let me give three variants:

A) 由 Java 开发人员选择:

A) as chosen by the Java developers:

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

行为方式相同,并且在数组和集合之间高度一致.然而,迭代器必须使用经典的迭代风格(至少不会导致错误).

The behaves the same way and is highly consistent across arrays and collections. Iterators however have to use the classic iteration style (which at least is not likely to cause errors).

B) 允许数组和Iterators:

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

现在数组和集合不一致;但是数组和 ArrayList 密切相关,应该表现相同.现在,如果在任何时候,该语言将被 扩展 以制作例如数组实现了Iterable,就变得不一致了.

Now arrays and collections are inconsistent; but arrays and ArrayList are very closely related and should behave the same way. Now if at any point, the language is extended to make e.g. arrays implement Iterable, it becomes inconsistent.

C) 允许所有三个:

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

现在,如果我们最终处于不清楚的情况下,当有人实现 both IterableIterator 时(for 循环是否应该得到一个新的迭代器或迭代当前 - 在树状结构中很容易发生!?!).不幸的是,一个简单的 tie-braker ala Iterable beats Iterator"是行不通的:它突然引入了运行时与编译时间差异和泛型问题.

Now if we end up in unclear situations when either someone implements both 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.

现在突然间,我们需要注意是要迭代集合/可迭代对象还是数组,此时我们以大混乱为代价获得了很少的好处.

Now suddenly, we need to pay attention to whether we want to iterate over collections/iterables or arrays, at which point we have gained very little benefits at the cost of a big confusion.

Java (A) 中for each"的方式非常一致,它导致的编程错误非常少,并且允许将来可能将数组转换为常规对象.

The way "for each" is in Java (A) is very consistent, it causes very little programming errors, and it allows for the possible future change of turning arrays into regular objects.

有一个变体 D) 可能也可以正常工作:for-each 仅适用于迭代器.最好通过向原始数组添加 .iterator() 方法:

There is a variant D) that would probably also work okay: for-each for Iterators only. Preferrably by adding a .iterator() method to primitive arrays:

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

但这需要更改运行时环境,而不仅仅是编译器,并且会破坏向后兼容性.另外,上面提到的混淆仍然存在,

But this requires changes to the runtime environment, not just the compiler, and breaks backwards compatibility. Plus, the mentioned confusion is still present that

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

只对数据进行一次迭代.

Only iterates over the data once.

相关文章