Programing

왜 Java의 Iterator가 Iterable이 아닌가?

lottogame 2020. 5. 26. 07:44
반응형

왜 Java의 Iterator가 Iterable이 아닌가?


Iterator인터페이스가 확장되지 Iterable않습니까?

iterator()메소드는 단순히를 반환 할 수 this있습니다.

Java 디자이너의 의도적이거나 단순한 감독입니까?

다음과 같은 반복자와 함께 for-each 루프를 사용하는 것이 편리합니다.

for(Object o : someContainer.listSomeObjects()) {
    ....
}

여기서 listSomeObjects()반복자를 반환합니다.


반복자는 일반적으로 컬렉션의 단일 인스턴스를 가리 킵니다. Iterable은 객체에서 요소를 가로 지르기 위해 객체에서 반복자를 얻을 수 있음을 의미하며 단일 인스턴스를 반복 할 필요가 없습니다. 이터레이터가 나타내는 것입니다.


반복자는 상태 저장입니다. 아이디어는 Iterable.iterator()두 번 호출 하면 독립적 인 반복자를 얻을 수 있다는 것 입니다. 귀하의 시나리오에서는 그렇지 않습니다.

예를 들어, 나는 보통 다음과 같이 쓸 수 있습니다.

public void iterateOver(Iterable<String> strings)
{
    for (String x : strings)
    {
         System.out.println(x);
    }
    for (String x : strings)
    {
         System.out.println(x);
    }
}

컬렉션을 두 번 인쇄해야하지만 스키마를 사용하면 두 번째 루프가 항상 즉시 종료됩니다.


내 $ 0.02의 경우, Iterator가 Iterable을 구현해서는 안된다는 것에 전적으로 동의하지만, 향상된 for 루프는 어느 쪽이든 받아 들여야한다고 생각합니다. 나는 "반복자를 반복 가능하게 만든다"는 주장이 언어의 결함에 대한 해결책으로 나온다고 생각한다.

향상된 for 루프가 도입 된 이유는 "컬렉션과 배열을 반복 할 때 반복자와 인덱스 변수의 혼란과 오류가 발생하지 않기 때문입니다"[ 1 ].

Collection<Item> items...

for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
    Item item = iter.next();
    ...
}

for (Item item : items) {
    ...
}

그렇다면 왜이 같은 주장이 반복자에게 적용되지 않습니까?

Iterator<Iter> iter...
..
while (iter.hasNext()) {
    Item item = iter.next();
    ...
}

for (Item item : iter) {
    ...
}

두 경우 모두 hasNext () 및 next ()에 대한 호출이 제거되었으며 내부 루프의 반복자에 대한 참조가 없습니다. 예, Iterables는 여러 반복기를 만드는 데 재사용 할 수 있지만 모든 루프 외부에서 발생한다는 것을 알고 있습니다. 루프 내부에서는 반복자가 반환 한 항목에 대해 한 번에 한 항목 씩 앞으로 만 진행됩니다.

또한 이것을 허용하면 다른 곳에서 지적했듯이 반복 가능 for 루프를 사용하는 것이 쉬워집니다.이 루프는 반복 가능하지 않은 반복자와 유사합니다.

따라서 Iterator를 Iterable로 구현하지 말고 for 루프를 업데이트하여 둘 중 하나를 수락하십시오.

건배,


마찬가지로 다른 사람에 의해 지적, Iterator그리고 Iterable다른 두 가지입니다.

또한 Iterator구현은 향상된 for 루프보다 우선합니다.

정적 메소드 가져 오기와 함께 사용할 때 다음과 같은 간단한 어댑터 메소드를 사용하여이 제한 사항을 극복하는 것도 쉽지 않습니다.

for (String line : in(lines)) {
  System.out.println(line);
}

샘플 구현 :

  /**
   * Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
   * loops. If {@link Iterable#iterator()} is invoked more than once, an
   * {@link IllegalStateException} is thrown.
   */
  public static <T> Iterable<T> in(final Iterator<T> iterator) {
    assert iterator != null;
    class SingleUseIterable implements Iterable<T> {
      private boolean used = false;

      @Override
      public Iterator<T> iterator() {
        if (used) {
          throw new IllegalStateException("SingleUseIterable already invoked");
        }
        used = true;
        return iterator;
      }
    }
    return new SingleUseIterable();
  }

Java 8에서는 an Iterator적응하는 것이 Iterable더 간단합니다.

for (String s : (Iterable<String>) () -> iterator) {

다른 사람들이 말했듯이, Iterable은 여러 번 호출 될 수 있으며 각 호출마다 새로운 Iterator를 반환합니다. 반복자는 한 번만 사용됩니다. 그래서 그들은 관련되어 있지만 다른 목적으로 사용됩니다. 그러나 "compact for"방법은 반복적으로 만 작동합니다.

아래에서 설명하는 것은 두 데이터를 모두 활용하는 한 가지 방법입니다. 기본 데이터 시퀀스가 ​​일 회일 때도 (더 나은 구문을 위해) Iterable을 반환합니다.

트릭은 실제로 작업을 트리거하는 Iterable의 익명 구현을 리턴하는 것입니다. 따라서 일회성 시퀀스를 생성 한 다음 반복자를 반환하는 작업을 수행하는 대신 액세스 할 때마다 작업을 다시 실행하는 Iterable을 반환합니다. 그것은 낭비 적 인 것처럼 보일 수도 있지만, 어쨌든 한 번만 Iterable을 호출 할 것입니다. 여러 번 호출하더라도 여전히 합리적인 의미를 갖습니다 (Iterator를 "Iterable처럼 보이게하는 간단한 래퍼와 달리" 두 번 사용하면 실패합니다).

예를 들어, 데이터베이스에서 일련의 객체를 제공하는 DAO가 있고 반복자를 통해 액세스 할 수 있도록하려고합니다 (예 : 필요하지 않은 경우 메모리에 모든 객체를 생성하지 않기 위해). 이제 반복자를 반환 할 수는 있지만 반환 된 값을 루프에서 못 생겼습니다. 따라서 대신 모든 것을 Anon Iterable에 래핑합니다.

class MetricDao {
    ...
    /**
     * @return All known metrics.
     */
    public final Iterable<Metric> loadAll() {
        return new Iterable<Metric>() {
            @Override
            public Iterator<Metric> iterator() {
                return sessionFactory.getCurrentSession()
                        .createQuery("from Metric as metric")
                        .iterate();
            }
        };
    }
}

그러면 다음과 같은 코드에서 사용될 수 있습니다.

class DaoUser {
    private MetricDao dao;
    for (Metric existing : dao.loadAll()) {
        // do stuff here...
    }
}

그래도 증분 메모리 사용을 유지하면서 컴팩트 한 for 루프를 사용할 수 있습니다.

이 접근 방식은 "게으른"것입니다. Iterable이 요청 될 때 작업이 완료되지 않고 나중에 내용이 반복 될 때만 수행되며 그 결과를 알고 있어야합니다. DAO가있는 예에서 이는 데이터베이스 트랜잭션 내에서 결과를 반복한다는 의미입니다.

따라서 여러 가지 경고가 있지만 여전히 많은 경우에 유용한 관용구가 될 수 있습니다.


Incredibly, no one else has given this answer yet. Here's how you can "easily" iterate over an Iterator by using the new Java 8 Iterator.forEachRemaining() method:

Iterator<String> it = ...
it.forEachRemaining(System.out::println);

Of course, there's a "simpler" solution that works with the foreach loop directly, wrapping the Iterator in an Iterable lambda:

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);

Iterator is an interface which allows you to iterate over something. It is an implementation of moving through a collection of some kind.

Iterable is a functional interface which denotes that something contains an accessible iterator.

In Java8, this makes life pretty easy... If you have an Iterator but need an Iterable you can simply do:

Iterator<T> someIterator;
Iterable<T> = ()->someIterator;

This also works in the for-loop:

for (T item : ()->someIterator){
    //doSomething with item
}

I also see many doing this:

public Iterator iterator() {
    return this;
}

But that does not make it right! This method would not be what you want!

The method iterator() is supposed to return a new iterator starting from scratch. So one need to do something like this:

public class IterableIterator implements Iterator, Iterable {

  //Constructor
  IterableIterator(IterableIterator iter)
  {
    this.initdata = iter.initdata;
  }
  // methods of Iterable

  public Iterator iterator() {
    return new MyClass(this.somedata);
  }

  // methods of Iterator

  public boolean hasNext() {
    // ...
  }

  public Object next() {
    // ...
  }

  public void remove() {
    // ...
  }
}

The question is: would there be any way to make an abstract class performing this? So that to get an IterableIterator one only need to implement the two methods next() and hasNext()


If you came here in search of a workaround, you can use IteratorIterable. (available for Java 1.6 and above)

Example usage (reversing a Vector).

import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
    public static void main(String ... args) {
        Vector<String> vs = new Vector<String>();
        vs.add("one");
        vs.add("two");
        for ( String s: vs ) {
            System.out.println(s);
        }
        Iterable<String> is
            = new IteratorIterable(new ReverseListIterator(vs));
        for ( String s: is ) {
            System.out.println(s);
        }
    }
}

prints

one
two
two
one

I agree with the accepted answer, but want to add my own explanation.

  • Iterator represents the state of traversing, e.g., you can get the current element from an iterator and move forward to the next.

  • Iterable represents a collection which could be traversed, it can return as many iterators as you want, each representing its own state of traversing, one iterator may be pointing to the first element, while another may be pointing to the 3rd element.

It would be nice if Java for loop accepts both Iterator and Iterable.


For the sake of simplicity, Iterator and Iterable are two distinct concepts, Iterable is simply a shorthand for "I can return an Iterator". I think that your code should be:

for(Object o : someContainer) {
}

with someContainer instanceof SomeContainer extends Iterable<Object>


As an aside: Scala has a toIterable() method in Iterator. See scala implicit or explicit conversion from iterator to iterable


On a related note, you may find the IteratorIterable adapter in Apache Commons Collections4 useful. Just create an instance from an iterator, and you have the corresponding iterable.

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html

ID: org.apache.commons:commons-collections4:4.0


Iterators are stateful, they have a "next" element and become "exhausted" once iterated over. To see where the problem is, run the following code, how many numbers are printed?

Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);

To avoid dependence on java.util package

According to the original JSR, An enhanced for loop for the Java™ Programming Language, the proposed interfaces:

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator
    (proposed to be retrofitted onto java.util.Iterator, but apparently this never happened)

java.lang보다 패키지 네임 스페이스 를 사용하도록 설계되었습니다 java.util.

JSR을 인용하려면 :

이 새로운 인터페이스는 java.util에 대한 언어의 종속성을 방지하는 역할을합니다.


그건 그렇고, 이전 java.util.IterableforEach람다 구문과 함께 사용하기 위해 Java 8 이상에서 새로운 메소드를 얻었습니다 (a 전달 Consumer).

다음은 예입니다. 모든 목록은 메소드를 전달하므로 인터페이스 List인터페이스를 확장 합니다.IterableforEach

List
.of ( "dog" , "cat" , "bird" )
.forEach ( ( String animal ) -> System.out.println ( "animal = " + animal ) );

다음 예제를 시도해 볼 수 있습니다.

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}

참고 URL : https://stackoverflow.com/questions/839178/why-is-javas-iterator-not-an-iterable

반응형