UFO ET IT

Java가 반복자 (반복자에만 해당)에서 foreach를 허용하지 않는 이유는 무엇입니까?

ufoet 2020. 11. 8. 11:37
반응형

Java가 반복자 (반복자에만 해당)에서 foreach를 허용하지 않는 이유는 무엇입니까?


중복 가능성 :
Java의 Iterator가 Iterable이 아닌 이유는 무엇입니까?

반복자가 주어진 for-each 루프를 사용하는 관용적 방법?

반복자 유형의 객체를 반복하기 위해 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
}

그러나 처음에 iterable이없고 iterator 만있는 경우 (예 : 클래스가 두 개의 다른 반복기를 제공하기 때문에), 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;
    }
}

(사실 한 번만 반복 할 수 있기 때문에 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.

언어가 이런 식으로 설계된 이유를 아는 사람 이 있습니까? 클래스가 IteratorIterable?를 모두 구현하는 경우 모호성을 피하기 위해 "for (O o : iter)"가 모든 요소를 ​​두 번 처리한다고 가정하는 프로그래머 오류를 방지하려면 (새 반복자를 얻는 것을 잊으십시오)? 아니면 다른 이유가 있습니까?

아니면 내가 모르는 언어 속임수가 있습니까?


언어가 이런 식으로 설계된 이유를 아는 사람 이 있습니까?

대한-각은 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`
}

나는 반복자로 시작한다.

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

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

두 번째 예제에서는 그다지 많지 않으며 특별한 경우를 만들어 for-each의 의미를 복잡하게 만듭니다.

"왜"질문은 결정에 참여한 주요 참가자를 대상으로하지 않을 때 항상 어렵지만, 추가 된 복잡성은 한계 효용의 가치가 없다고 생각합니다.


즉, "향상된 while루프"구조를 볼 수 있습니다 .

while (Thingy t : iterator) {
   // Use `t`
}

...which picks up where the iterator currently is... Meh, maybe it would confuse people too much. :-)


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:

A) as chosen by the Java developers:

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:

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.

C) allow all three:

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.

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.

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:

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.


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.

Regarding the new interfaces discussed in the JSR (note the package names):

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator (proposed in JSR to be retrofitted onto java.util.Iterator though not actually done)

…the JSR says:

These new interfaces serve to prevent the dependency of the language on java.util that would otherwise result.


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.

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.

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:

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.


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

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.

참고URL : https://stackoverflow.com/questions/11216994/why-does-java-not-allow-foreach-on-iterators-only-on-iterables

반응형