Почему итератор Java не является итерируемым?


почему Iterator интерфейс не распространяется Iterable?

The iterator() метод может просто вернуть this.

это специально или просто недосмотр дизайнеров Java?

было бы удобно использовать цикл for-each с такими итераторами:

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

здесь listSomeObjects() возвращает итератор.

15 161

15 ответов:

потому что итератор обычно указывает на один экземпляр в коллекции. Iterable подразумевает, что можно получить итератор от объекта для обхода его элементов - и нет необходимости повторять один экземпляр, который представляет собой итератор.

итератор является stateful. Идея в том, что если вы позвоните 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: внутри цикла есть только одна прямая прогрессия по одному элементу за раз над элементами, возвращенными итератор.

кроме того, разрешение этого также позволит легко использовать цикл for для перечислений, которые, как было указано в другом месте, аналогичны итераторам, а не итерациям.

поэтому не делайте итератор реализовать Iterable, но обновить цикл for, чтобы принять либо.

спасибо,

как указывали другие,Iterator и Iterable это две разные вещи.

и Iterator реализации предшествуют улучшенным циклам.

также тривиально преодолеть это ограничение с помощью простого метода адаптера, который выглядит так при использовании со статическим методом импорта:

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 адаптация Iterator до Iterable получает проще:

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

Как уже говорили другие, итерацию можно вызывать несколько раз, возвращая новый итератор при каждом вызове; итератор используется только один раз. Так что они связаны, но служат разным целям. К сожалению, однако, метод "compact for" работает только с итерацией.

то, что я опишу ниже, - это один из способов получить лучшее из обоих миров-возвращение итеративного (для более приятного синтаксиса), даже когда базовая последовательность данных одноразовая.

фокус в том, чтобы возвращает анонимную реализацию 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 это означает итерацию результатов в транзакции базы данных.

таким образом, есть различные предостережения, но это все еще может быть полезной идиомой во многих случаях.

Iterator - это интерфейс, который позволяет перебирать что-то. Это реализация перемещения через какую-то коллекцию.

Iterable - это функциональный интерфейс, который означает, что что-то содержит доступный итератор.

в Java8, это делает жизнь довольно легко... Если у вас есть Iterator а нужно Iterable вы можете просто сделать:

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

это также работает в for-loop:

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

Невероятно, но никто еще не дал этот ответ. Вот как вы можете "легко" перебирать Iterator С помощью новой Java 8 Iterator.forEachRemaining() способ:

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

конечно, есть "более простое" решение, которое работает с циклом foreach напрямую, обертывая Iterator на Iterable лямбда:

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

Я также вижу, что многие делают это:

public Iterator iterator() {
    return this;
}

но это не делает это правильно! Этот метод не будет то, что вы хотите!

метод iterator() предполагается возвращает новый итератор, начиная с нуля. Поэтому нужно сделать что-то вроде этого:

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() {
    // ...
  }
}

вопрос в том, есть ли способ сделать абстрактный класс, выполняющий это? Так что для получения IterableIterator нужно только реализовать два метода next () и hasNext ()

Если вы пришли сюда в поисках обходного пути, вы можете использовать IteratorIterable. (доступно для Java 1.6 и выше)

пример использования (реверсирование вектора).

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

печать

one
two
two
one

для простоты, Iterator и Iterable-это два разных понятия, Iterable-это просто сокращение Для "я могу вернуть итератор". Я думаю, что ваш код должен быть:

for(Object o : someContainer) {
}

С someContainer instanceof SomeContainer extends Iterable<Object>

в стороне: Scala имеет toIterable () метод в итераторе. Смотрите Scala неявное или явное преобразование из итератора в iterable

в соответствующей заметке вы можете найти полезный адаптер IteratorIterable в Apache Commons Collections4. Просто создать экземпляр из итератора, и у вас есть соответствующий метод.

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

ID: org.апаш.commons:commons-collections4: 4.0

итераторы являются статусными, они имеют "следующий" элемент и становятся "исчерпанными" после повторения. Чтобы увидеть, где проблема, выполните следующий код, сколько чисел печатается?

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

Я согласен с принятым ответом, но хочу добавить свое собственное объяснение.

  • итератор представляет состояние обхода, например, вы можете получить текущий элемент из итератора и перейти к следующему.

  • Iterable представляет коллекцию, которая может быть пройдена, она может возвращать столько итераторов, сколько вы хотите, каждый из которых представляет свое собственное состояние обхода, один итератор может указывать на первый элемент, в то время как другой может указывает на 3-й элемент.

было бы неплохо, если Java for loop принимает как итератор, так и итерацию.

вы можете попробовать следующий пример :

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