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

Ano*_*sse 68 java syntax iterator iterable language-design

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

在给定迭代器的情况下使用for-each循环的惯用方法?

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

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

Iterable<O> iterable;
for(O o : iterable) {
    // Do something
}
Run Code Online (Sandbox Code Playgroud)

将基本上产生相同的字节码

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

但是,如果我首先没有迭代,但只有一个迭代器(比如,因为一个类提供了两个不同的迭代器),我不能使用语法sugar foreach循环.显然我仍然可以做普通的旧式迭代.但是,我其实想做:

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

当然我可以做假的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

(实际上这是对Iterable 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.
Run Code Online (Sandbox Code Playgroud)

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

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

T.J*_*der 36

有谁知道为什么语言是这样设计的?

因为,每个才有过的事情是ITER感,并没有什么意义了ITER ators.如果您已经有一个迭代器,那么您已经拥有了一个简单的循环所需要的东西.

比较:我开始了与国际热核实验堆能够:

// Old way
Iterator<Thingy> it = iterable.iterator();
while (it.hasNext()) {
    Thingy t = it.next();
    // Use `t`
}

// New way
for (Thingy t : iterable) {
    // Use `t`
}
Run Code Online (Sandbox Code Playgroud)

我开始使用迭代器:

// Old/current way
while (iterator.hasNext()) {
    Thing t = iterator.next();
    // Use `t`
}

// Imagined way
for (Thingy t : iterator) {
   // Use `t`
}
Run Code Online (Sandbox Code Playgroud)

在第二个例子中没有太多内容,它通过创建一个特殊情况使for-each的语义复杂化.

当没有针对参与决策的主要参与者时,"为什么"问题总是很难,但我的猜测是增加的复杂性不值得边际效用.


也就是说,我可以看到一个"增强的while循环"结构:

while (Thingy t : iterator) {
   // Use `t`
}
Run Code Online (Sandbox Code Playgroud)

......它取决于迭代器目前的位置......嗯,也许它会让人太困惑.:-)

  • 不要看到这个论点.如果你使用for循环而不是while来编写"旧方法",你会得到几乎相同的代码.如果"旧方式"迭代器版本看起来更直观,请举手.一个更好的论据是for循环具有隐含的破坏性,这可能是令人惊讶的 - 尽管我仍然不喜欢它. (14认同)
  • 不允许使用迭代器,我看不到*任何*的好处.你实际上只能*允许迭代器,你可以通过执行`for(Thing a:iterable.iterator())`只需10个字符附加内容来使用iterable,你可以在这两种情况下使用这种语法. (13认同)
  • 这个答案没有任何意义.他们可能刚刚更改了迭代器的交互面,这样迭代器也必须是Iterable并将自己作为迭代器返回.他们甚至可以在现代界面中使用默认方法进行大量更改. (7认同)
  • *"我不认为不允许迭代器有任何好处."* - 你必须紧紧闭上眼睛:-).显而易见,有一些好处......例如避免语言复杂性和复杂性引起的可用性问题.并且*"它不会伤害"*不是事实陈述.这是一份意见陈述. (5认同)
  • @ Anony-Mousse:语言设计中有很多东西"不会伤害"."不会受伤"通常不是一个足够好的论据.你需要*积极的利益*,通常它不仅仅是一个微不足道的痛苦.:-)语言设计,一旦语言在野外,必须成为一个非常保守的东西. (4认同)
  • 与“迭代器”相对,为什么只对“可迭代”的东西有意义?我显然可以做一些事情“为此迭代器返回的每个元素”。 (2认同)

Ano*_*sse 20

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

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

如果语法是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(); }
Run Code Online (Sandbox Code Playgroud)

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

B)允许数组和Iterators:

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

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

C)允许所有三个:

Object[] array;
for(Object o : array) { }
Iterable<Object> list;
for(Object o : list) { }
Iterator<Object> iter;
for(Object o : iter) { }
Run Code Online (Sandbox Code Playgroud)

现在,如果我们最终处于不明确的情况,当有人实现两者 Iterable并且Iterator(for循环应该获得新的迭代器或迭代当前 - 在树状结构中很容易发生!?!).一个简单的tie-braker ala"Iterable beats Iterator"遗憾的是不会这样做:它突然引入了运行时与编译时差和泛型问题.

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

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

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

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

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

Iterator<Object> iter;
for(Object o : iter) { }
for(Object o : iter) { }
Run Code Online (Sandbox Code Playgroud)

仅迭代数据一次.


ass*_*ias 9

Iterable接口是为了这个目的而创建的(增强了for循环),如原始JSR中所述,尽管Iterator接口已经在使用中.

  • 显然,引入`java.lang.Iterable`而不是仅使用`java.util.Iterator`的基本原理是避免对`java.util`的核心语言依赖.;) (3认同)
  • 考虑到循环仍然依赖于刚刚从“Iterable”的“iterator()”方法返回的“java.util.Iterator”类型,这是一个奇怪的基本原理。 (2认同)

Mat*_*att 7

因为"for"循环对迭代器是破坏性的.迭代器不能被重置(即,移回到开头),除非它实现了ListIterator子接口.

一旦你通过"for"循环放置Iterator它将不再可用.我的猜测是语言设计者决定在编译器中结合其他特殊情况(其中已经有两个用于Iterable和数组)将其转换为字节码(你不能重用与iterable相同的转换)就足够了一个没有实现它的detractor.

当你通过迭代器接口自己在代码中执行此操作时,它至少会明显地显示正在发生的事情.

随着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); } );
Run Code Online (Sandbox Code Playgroud)

不破坏向后兼容性,并且仍然具有相对简单的语法.我怀疑他们会将"each","collect"和"map"之类的方法构建到不同的接口中,因为这会破坏向后兼容性,而且你还有数组仍在处理.


归档时间:

查看次数:

72113 次

最近记录:

6 年,4 月 前