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

时间:2021-12-22 19:15:44

Possible Duplicate:
Why is Java's Iterator not an Iterable?

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

Idiomatic way to use for-each loop given an iterator?

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

Can we use for-each loop for iterating the objects of Iterator type?

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

The foreach loop are as far as I know syntax sugar added in Java 5. So

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

Iterable<O> iterable;
for(O o : iterable) {
    // Do something
}

will essentially produce the same bytecode as

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

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

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:

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

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

And of course I can do a fake Iterable:

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

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

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

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.

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?

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

Or is there some language trick I just do not know?

或者是否有一些我不知道的语言技巧?

5 个解决方案

#1


33  

Does anyone know why the language was designed this way?

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

Because for-each only makes sense over things that are iterable, and doesn't make sense over iterators. If you already have an iterator, you already have what you need to do this with a simple loop.

因为for-each只对可迭代的事物有意义,而且对迭代器没有意义。如果您已经有一个迭代器,那么您已经拥有了一个简单循环所需的功能。

Compare: I start out with an iterable:

比较:我从一个可迭代开始:

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

// New way
for (Thingy t : iterable) {
    // Use `t`
}

Versus I start out with an iterator:

我开始使用迭代器:

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

// Imagined way
for (Thingy t : iterator) {
   // Use `t`
}

There's not much in it in the second example, and it complicates the semantics of the for-each by creating a special case.

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

"Why" questions are always hard when not directed at the principal participants involved in the decision, but my guess is that the added complexity wasn't worth the marginal utility.

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

#2


17  

So I have a somewhat reasonable explanation now:

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

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

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

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

如果我提出的语法是围绕Iterator设计的,那么它将与数组不一致。让我给出三个变种:

A) as chosen by the Java developers:

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

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) allow arrays and Iterators:

B)允许数组和迭代器:

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

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.

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

C) allow all three:

C)允许所有三个:

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

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.

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

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.

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

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.

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

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:

有一个变体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) { }

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.

仅迭代数据一次。

#3


8  

The Iterable interface was created exactly for that purpose (enhanced for loop) as described in the original JSR, although the Iterator interface was already in use.

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

#4


6  

Because the "for" loop would be destructive to the iterator. Iterator cannot be reset (ie. moved back to the beginning) unless it implements the ListIterator subinterface.

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

Once you put an Iterator through a "for" loop it would no longer useable. My guess is the language designers decided that this combined with the additional special cases (of which there are already two for Iterable and arrays) in the compiler to translate this into bytecode (you couldn't reuse the same transformation as iterable) was enough of a detractor to not implement it.

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

When you do this yourself in the code via the iterator interface, it would at least be apparantly obvious what's going on.

当你通过迭代器接口自己在代码中执行此操作时,至少会明显地发生了什么。

With lambdas coming they could make this nice and easy:

随着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); } );

without breaking backward compatibility, and still having a relatively simple syntax. I doubt they would built methods like "each", "collect" and "map" into the different interfaces because that would break backward compatibilty, plus you'd have arrays still to deal with.

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

#5


0  

I think one part of the answer may be hidden in the fact that the for-each loop is syntactic sugar. The point being that you want to make something that people do a lot, a lot easier. And (at least in my experience) the idiom

我认为答案的一部分可能隐藏在for-each循环是语法糖的事实中。关键在于你想做一些人们做了很多事情,更容易。并且(至少在我的经验中)这个成语

Iterator iterator = iterable.iterator();
while( iterator.hasNext() ) {
  Element e = (Element)iterator.next() ;
}

occurred all the time in old-style code. And doing fancy things with multiple iterators did not.

在旧式代码中始终发生。用多个迭代器做奇特的东西却没有。

#1


33  

Does anyone know why the language was designed this way?

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

Because for-each only makes sense over things that are iterable, and doesn't make sense over iterators. If you already have an iterator, you already have what you need to do this with a simple loop.

因为for-each只对可迭代的事物有意义,而且对迭代器没有意义。如果您已经有一个迭代器,那么您已经拥有了一个简单循环所需的功能。

Compare: I start out with an iterable:

比较:我从一个可迭代开始:

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

// New way
for (Thingy t : iterable) {
    // Use `t`
}

Versus I start out with an iterator:

我开始使用迭代器:

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

// Imagined way
for (Thingy t : iterator) {
   // Use `t`
}

There's not much in it in the second example, and it complicates the semantics of the for-each by creating a special case.

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

"Why" questions are always hard when not directed at the principal participants involved in the decision, but my guess is that the added complexity wasn't worth the marginal utility.

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

#2


17  

So I have a somewhat reasonable explanation now:

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

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

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

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

如果我提出的语法是围绕Iterator设计的,那么它将与数组不一致。让我给出三个变种:

A) as chosen by the Java developers:

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

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) allow arrays and Iterators:

B)允许数组和迭代器:

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

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.

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

C) allow all three:

C)允许所有三个:

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

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.

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

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.

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

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.

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

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:

有一个变体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) { }

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.

仅迭代数据一次。

#3


8  

The Iterable interface was created exactly for that purpose (enhanced for loop) as described in the original JSR, although the Iterator interface was already in use.

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

#4


6  

Because the "for" loop would be destructive to the iterator. Iterator cannot be reset (ie. moved back to the beginning) unless it implements the ListIterator subinterface.

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

Once you put an Iterator through a "for" loop it would no longer useable. My guess is the language designers decided that this combined with the additional special cases (of which there are already two for Iterable and arrays) in the compiler to translate this into bytecode (you couldn't reuse the same transformation as iterable) was enough of a detractor to not implement it.

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

When you do this yourself in the code via the iterator interface, it would at least be apparantly obvious what's going on.

当你通过迭代器接口自己在代码中执行此操作时,至少会明显地发生了什么。

With lambdas coming they could make this nice and easy:

随着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); } );

without breaking backward compatibility, and still having a relatively simple syntax. I doubt they would built methods like "each", "collect" and "map" into the different interfaces because that would break backward compatibilty, plus you'd have arrays still to deal with.

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

#5


0  

I think one part of the answer may be hidden in the fact that the for-each loop is syntactic sugar. The point being that you want to make something that people do a lot, a lot easier. And (at least in my experience) the idiom

我认为答案的一部分可能隐藏在for-each循环是语法糖的事实中。关键在于你想做一些人们做了很多事情,更容易。并且(至少在我的经验中)这个成语

Iterator iterator = iterable.iterator();
while( iterator.hasNext() ) {
  Element e = (Element)iterator.next() ;
}

occurred all the time in old-style code. And doing fancy things with multiple iterators did not.

在旧式代码中始终发生。用多个迭代器做奇特的东西却没有。