Ano*_*sse 68 java syntax iterator iterable language-design
可能重复:
为什么Java的迭代器不是Iterable?
据我所知,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)
......它取决于迭代器目前的位置......嗯,也许它会让人太困惑.:-)
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)
仅迭代数据一次.
因为"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 次 |
| 最近记录: |