Почему итератор Java не является итерируемым?
Вопрос
Почему этот Iterator
интерфейс не расширяется Iterable
?
Тот Самый iterator()
метод мог бы просто вернуть this
.
Это сделано специально или просто по недосмотру разработчиков Java?
Было бы удобно иметь возможность использовать цикл for-each с такими итераторами, как этот:
for(Object o : someContainer.listSomeObjects()) {
....
}
где listSomeObjects()
возвращает итератор.
Решение
Потому что итератор обычно указывает на один экземпляр в коллекции. Итерируемость подразумевает, что можно получить итератор от объекта для обхода его элементов - и нет необходимости перебирать один экземпляр, который представляет собой итератор.
Другие советы
Итератор с состоянием. Идея состоит в том, что если вы дважды вызовете 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 для перечислений, которые, как было указано в другом месте, аналогичны итераторам, а не итерабельным.
Поэтому не заставляйте Iterator реализовывать Iterable, но обновите цикл for, чтобы он принимал либо то, либо другое.
Ваше здоровье,
Как указали другие, Iterator
и < a href = "http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"> Iterable
- это две разные вещи.
Кроме того, <=> улучшения предшествуют циклам.
Обойти это ограничение также тривиально с помощью простого метода адаптера, который выглядит следующим образом при использовании импорта статических методов:
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 адаптация <=> к <=> упрощается:
for (String s : (Iterable<String>) () -> iterator) {
Как уже говорили другие, Iterable может вызываться несколько раз, возвращая свежий Iterator при каждом вызове; Итератор используется только один раз. Таким образом, они связаны, но служат различным целям. Однако, к сожалению, & Quot; compact for & Quot; Метод работает только с итерацией.
То, что я опишу ниже, - это один из способов получить лучшее из обоих миров - вернуть Iterable (для лучшего синтаксиса), даже если базовая последовательность данных одноразовая.
Хитрость в том, чтобы вернуть анонимную реализацию Iterable, которая фактически запускает работу. Таким образом, вместо того, чтобы выполнять работу, которая генерирует одноразовую последовательность, а затем возвращать Итератор, вы возвращаете Итерируемый, который при каждом обращении к нему повторяет работу. Это может показаться расточительным, но часто вы все равно будете вызывать Iterable один раз, и даже если вы вызываете его несколько раз, он все еще имеет разумную семантику (в отличие от простой оболочки, которая делает Iterator & Quot; похожим на " a Iterable, это не даст сбоя, если его использовать дважды).
Например, скажем, у меня есть DAO, который предоставляет серию объектов из базы данных, и я хочу предоставить доступ к нему через итератор (например, чтобы избежать создания всех объектов в памяти, если они не нужны). Теперь я могу просто вернуть итератор, но это делает использование возвращаемого значения в цикле уродливым. Поэтому вместо этого я обертываю все в один итеративный:
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 для компакта, сохраняя при этом добавочное использование памяти.
Этот подход " lazy " - работа не выполняется, когда запрашивается Iterable, а только позже, когда содержимое перебирается - и вы должны знать о последствиях этого. В примере с DAO это означает итерацию результатов в транзакции базы данных.
Таким образом, существуют различные предупреждения, но во многих случаях это может быть полезной идиомой.
Невероятно, но еще никто не дал этот ответ. Вот как вы можете & Легко & Quot; переберите Iterator
с помощью новой Java 8 Iterator.forEachRemaining()
метод:
Iterator<String> it = ...
it.forEachRemaining(System.out::println);
Конечно, есть " проще " решение, которое работает с циклом foreach напрямую, заключая Iterable
в <=> лямбду:
for (String s : (Iterable<String>) () -> it)
System.out.println(s);
Iterator
это интерфейс, который позволяет вам перебирать что-то. Это реализация перемещения какой-то коллекции.
Iterable
- это функциональный интерфейс, который обозначает, что что-то содержит доступный итератор.
В Java8 это делает жизнь довольно легкой ... Если у вас есть <=>, но вам нужен <=>, вы можете просто это сделать:
Iterator<T> someIterator;
Iterable<T> = ()->someIterator;
Это также работает в цикле for:
for (T item : ()->someIterator){
//doSomething with item
}
Я также вижу, что многие так делают:
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 ()
Если вы пришли сюда в поисках обходного пути, вы можете использовать Итератор, поддающийся повторению.(доступно для 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
Я согласен с принятым ответом, но хочу добавить свое собственное объяснение.
Итератор представляет состояние обхода, например, вы можете получить текущий элемент из итератора и перейти к следующему.
Iterable представляет коллекцию, которая может быть пройдена, она может возвращать столько итераторов, сколько вы хотите, каждый из которых представляет свое собственное состояние прохождения, один итератор может указывать на первый элемент, в то время как другой может указывать на 3-й элемент.
Было бы неплохо, если бы Java for loop принимала как Iterator, так и Iterable.
Для простоты Iterator и Iterable - это две разные концепции, Iterable - это просто сокращение для " я могу вернуть Iterator " ;. Я думаю, что ваш код должен быть:
for(Object o : someContainer) {
}
с экземпляром someContainer SomeContainer extends Iterable<Object>
Кроме того: в Scala есть метод toIterable () в Iterator. См. неявное или явное преобразование scala из итератора в итеративный р>
В соответствующей заметке вы можете найти адаптер IteratorIterable в Apache Commons Collections4 полезным. Просто создайте экземпляр из итератора, и вы получите соответствующий итератор.
ID: org.apache.commons: commons-collection4: 4.0
Итераторы с состоянием, у них есть " next " элемент и стать " исчерпаны " однажды перебрал Чтобы увидеть, в чем проблема, запустите следующий код, сколько цифр напечатано?
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);
Вы можете попробовать следующий пример:
List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
System.out.println(iterator.next());
}