Почему 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>
Ответ 12
В стороне: Scala имеет метод toIterable() в Iterator. См. scala неявное или явное преобразование с итератора на итеративный
Ответ 13
В соответствующей заметке вы можете найти адаптер IteratorIterable в Apache Commons Collections4 полезным. Просто создайте экземпляр из итератора, и у вас есть соответствующий итерабель.
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html
ID: org.apache.commons: commons-collections4: 4.0
Ответ 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());
}