为什么Java不允许对迭代器进行foreach(仅在iterables上)?
可能重复:
为什么Java的迭代器不是可迭代的?每个循环给定一个迭代器的习惯用法?
我们可以使用for-each循环来迭代Iteratortypes的对象吗?
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 }
但是,如果我没有一个迭代器,但只有一个迭代器(比如,因为一个类提供了两个不同的迭代器),我不能使用语法糖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; } }
(这实际上是对Iterable API的一个丑陋的滥用,因为它只能迭代一次!)
如果它是围绕Iterator
devise的,而不是迭代的,那么可以做一些有趣的事情:
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.
有谁知道为什么这种语言是这样devise的? 为了避免歧义,如果一个类将实现Iterator
和Iterable
? 为了避免程序员错误,假设“for(O o:iter)”将处理所有元素两次(并忘记获得一个新的迭代器)? 还是有其他原因吗?
还是有一些语言技巧,我只是不知道?
有谁知道为什么这种语言是这样devise的?
因为对于每一件事都是有意义的,而且对于它们来说是没有意义的。 如果你已经有一个迭代器,你已经有了你需要做的一个简单的循环。
比较:我开始与iter 能够 :
// Old way Iterator<Thingy> it = iterable.iterator(); while (it.hasNext()) { Thingy t = it.next(); // Use `t` } // New way for (Thingy t : iterable) { // Use `t` }
与我从一个迭代器开始:
// Old/current way while (iterator.hasNext()) { Thing t = iterator.next(); // Use `t` } // Imagined way for (Thingy t : iterator) { // Use `t` }
第二个例子中没有太多的东西,它通过创build一个特殊的情况使for的每个语义复杂化。
“为什么”的问题总是很难,而不是针对参与决策的主要参与者,但我的猜测是增加的复杂性不值得边际效用。
所以我现在有一个合理的解释:
简短版本: 因为语法也适用于没有迭代器的数组 。
如果我所提出的语法是围绕着Iterator
来devise的,那么它将与数组不一致。 让我给三个变种:
A)由Java开发者select:
Object[] array; for(Object o : array) { } Iterable<Object> list; for(Object o : list) { } Iterator<Object> iter; while(iter.hasNext()) { Object o = iter.next(); }
行为方式相同,在数组和集合之间高度一致 。 但是迭代器必须使用经典的迭代风格(至less不会导致错误)。
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) { }
现在,如果我们在不确定的情况下结束时,有人实现了 Iterable
和Iterator
(for循环应该得到一个新的迭代器或迭代当前 – 很容易发生树状结构!?!)。 一个简单的tie-braker ala“Iterable Beats Iterator”不幸的是不会这么做:它突然引入了运行时间与编译时间差异和generics问题。
现在突然间,我们需要关注是否要迭代集合/迭代器或数组,在这一点上,我们以很大的困惑为代价获得了很less的好处。
Java(A)中的“for each”方式是非常一致的,它会导致很less的编程错误,并且允许未来将数组转换为常规对象。
有一个变种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) { }
只迭代一次数据。
Iterable接口是为原来的目的创build的(增强的for循环),尽pipeIterator接口已经在使用了。
因为“for”循环对迭代器是破坏性的。 迭代器不能被重置(即移回到开始),除非它实现了ListIterator子接口。
一旦你把一个迭代器通过“for”循环,它将不再可用。 我的猜测是,语言devise者认为,这与编译器中的其他特殊情况(其中有两个用于Iterable和数组)已经将其转换为字节码(不能重复使用与迭代相同的转换)足够了一个诋毁者不执行它。
当你通过迭代器接口在代码中自己做这件事的时候,至less可以明显地看出发生了什么。
随着lambda的来临,他们可以使这个很好,很容易:
Iterator<String> iterator = ...; Collections.each ( iterator, (String s) => { System.out.println(s); } ); List<String> list = ...; Collections.each ( list, (String s) => { System.out.println(s); } );
而不会破坏后向兼容性,并且仍然具有相对简单的语法。 我怀疑他们会build立像“each”,“collect”和“map”到不同接口的方法,因为这会破坏向后兼容性,再加上你还有数组需要处理。
我认为答案的一部分可能隐藏在for-each循环是句法糖的事实中。 重要的是你想要让人们做很多事情,那就容易多了。 (至less以我的经验)这个成语
Iterator iterator = iterable.iterator(); while( iterator.hasNext() ) { Element e = (Element)iterator.next() ; }
在旧式的代码中一直发生。 而用多个迭代器做花哨的事情没有。