为什么Java不允许在迭代器上foreach(只在可迭代器上)?

可能的重复:
为什么Java的迭代器不是可迭代的?

使用给定迭代器的每个循环的惯用方式?

我们可以使用for-each循环来迭代迭代器类型的对象吗?

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

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
}

但是,如果我一开始没有可迭代,而只有一个迭代器(例如,因为一个类提供了两个不同的迭代器),我就不能使用语法 sugar foreach 循环。显然,我仍然可以做普通的旧式迭代。但是,我实际上想做:

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

当然,我可以做一个假的: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;
    }
}

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

如果它是围绕而不是可迭代设计的,那么人们可以做一些有趣的事情:Iterator

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.

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

还是有一些我不知道的语言技巧?


答案 1

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

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

如果语法是按照我的建议设计的,那么它将与数组不一致。让我给出三个变体: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) 允许数组和: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) { }

现在,如果我们最终遇到不清楚的情况,当有人同时实现两者并且(for循环是否应该获得新的迭代器或迭代电流时 - 在树状结构中很容易发生!?!)。不幸的是,一个简单的并列制动器“Iterable beats Iterator”将不起作用:它突然引入了运行时与编译时间差和泛型问题。IterableIterator

现在突然之间,我们需要注意我们是否要迭代集合/可迭代对象或数组,此时我们以巨大的混乱为代价获得了很少的收益。

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

有一个变体D)可能也可以正常工作:对于迭代器,每个变体都可以。最好通过向基元数组添加一个方法来:.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) { }

仅循环访问数据一次。


答案 2

迭代接口正是为此目的而创建的(针对循环进行了增强),如原始 JSR 中所述,尽管迭代器接口已经在使用中。

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

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator(在JSR中提议进行改造,尽管实际上没有完成)java.util.Iterator

...JSR 说:

这些新接口用于防止语言依赖于否则会导致这种情况。java.util