Почему Java Iterator не является Iterable?

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

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

Это специально или просто надзор над разработчиками Java?

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

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

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

Ответ 1

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

Ответ 2

Итератор имеет состояние. Идея состоит в том, что если вы назовете Iterable.iterator() дважды, вы получите независимые итераторы - для большинства итераций. Это явно не соответствует вашему сценарию.

Например, я обычно пишу:

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

Это должно печатать коллекцию дважды - но с вашей схемой второй цикл всегда заканчивается мгновенно.

Ответ 3

За мои 0,02 доллара я полностью согласен с тем, что Iterator не должен реализовывать Iterable, но я думаю, что для цикла loop должен быть принят. Я думаю, что весь аргумент "сделать итераторы итерируемый" возникает как работа с дефектом на языке.

Вся причина внедрения расширенного цикла 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 для перечислений, которые, как указано в другом месте, аналогичны Iterators not Iterables.

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

Приветствия,

Ответ 4

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

Кроме того, Iterator реализация predate улучшена для циклов.

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

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

Ответ 5

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

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

Хитрость заключается в возврате анонимной реализации Iterable, которая фактически запускает работу. Поэтому вместо выполнения работы, которая генерирует одноразовую последовательность, а затем возвращает Итератор над этим, вы возвращаете Iterable, который при каждом обращении выполняет повторную работу. Это может показаться расточительным, но часто вы будете называть Iterable один раз в любом случае, и даже если вы его вызываете несколько раз, у него все еще есть разумная семантика (в отличие от простой обертки, которая делает Итератор "похожим на" Iterable ", t сбой при использовании дважды).

Например, скажем, у меня есть 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...
    }
}

который позволяет мне использовать цикл compact для цикла, сохраняя при этом инкрементную память.

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

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

Ответ 6

Невероятно, но еще никто не дал этот ответ. Вот как вы можете "легко" перебирать 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);

Ответ 7

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

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

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

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

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

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

Ответ 8

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

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

Ответ 9

Если вы пришли сюда в поисках обходного пути, вы можете использовать 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

Ответ 10

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

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

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

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

Ответ 11

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

for(Object o : someContainer) {
}

с someContainer instanceof SomeContainer extends Iterable<Object>

Ответ 14

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

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

Ответ 15

Чтобы избежать зависимости от пакета java.util

Согласно исходному JSR, расширенный цикл for для языка программирования Java ™, предлагаемые интерфейсы:

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator
    (предлагается установить на java.util.Iterator, но, видимо, этого не произошло)

… Были разработаны для использования пространства имен пакета java.lang, а не java.util.

Процитирую JSR:

Эти новые интерфейсы служат для предотвращения зависимости языка от java.util, которая в противном случае могла бы привести.


Кстати, старый java.util.Iterable получил новый метод forEach в Java 8+ для использования с лямбда-синтаксисом (передавая Consumer).

Вот пример. Интерфейс List расширяет интерфейс Iterable, поскольку любой список содержит метод forEach.

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

Ответ 16

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

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