Можем ли мы использовать цикл for-each для итерации объектов типа Iterator? - PullRequest
1 голос
/ 29 января 2010

Если мы сделаем следующее, мы получим ошибку:

class FGH{
public static Iterator reverse(List list) {
     Collections.reverse(list);
     return  list.iterator();
     }
     public static void main(String[] args) {
     List list = new ArrayList();
     list.add("1"); list.add("2"); list.add("3");
     /*for(Iterator it:reverse(list))
     Iterator it=reverse(list);*/
     for (Object obj: reverse(list))
     System.out.print(obj + ", ");}}

но если мы модифицируем код таким образом, мы не получим ошибку, значит ли это, что мы не можем перебирать объекты типа Iterator? :

class FGH{
public static Iterator reverse(List list) {
     Collections.reverse(list);
     return  list.iterator();
     }
     public static void main(String[] args) {
     List list = new ArrayList();
     list.add("1"); list.add("2"); list.add("3");
     Iterator it=reverse(list);
     while(it.hasNext()){
    Object obj=it.next();
    System.out.println(obj);
     }
     }}

Ответы [ 6 ]

5 голосов
/ 29 января 2010

Цикл for в вашем первом примере предполагает, что reverse(list) - это набор Iterator, что, конечно, не так. Вот почему этот пример не сработает.

Как правило, foreach можно использовать только для классов, которые реализуют Iterable. Iterator не является одним из этих классов.

См. http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Iterable.html

4 голосов
/ 29 января 2010

Многие ответы говорят о том, что итераторы не являются итераторами. Это правда, но такой ответ не касается почему.

Причина того, что для каждого цикла требуется итерация, состоит в том, что один и тот же объект может быть пройден несколько раз (так что вы можете использовать несколько циклов для каждого для одного и того же объекта без неожиданного поведения), тогда как итератор допускает только один обход , Если для каждого допускается использование итераторов, такое поведение будет удивительным для программистов, которые не понимают, что их итераторы будут исчерпаны после запуска цикла.

Если вы используете API, который предоставляет только итераторы, и вы хотите использовать итераторы, у вас есть два способа решить эту проблему:

  1. Создайте анонимный итерируемый класс, метод которого iterator() вызывает функцию API, которая возвращает итератор. Таким образом, каждый раз, когда вы используете цикл for-each в итерируемом объекте, эта функция API вызывается снова, возвращая новый (и неисчерпанный) итератор.
  2. Создает однопроходный итеративный класс-обертку, который принимает итератор и допускает один вызов iterator(). При последующих вызовах выведите AssertionError или IllegalStateException в зависимости от ситуации.
0 голосов
/ 21 декабря 2016

Вы должны сначала обернуть Iterator в Iterable.Вот решение Java 8:

for (Object obj : (Iterable<Object>) () -> reverse(list))
    System.out.print(obj + ", ");

Или просто используйте Iterator.forEachRemaining():

reverse(list).forEachRemaining(obj -> System.out.print(obj + ", "));
0 голосов
/ 27 июня 2012

Синтаксис «для каждого» предназначен для коллекций (точнее, Iterable), а не для итераторов. Я пытаюсь выяснить аргументы того, почему Java была разработана таким образом в этом вопросе .

Самое простое решение для вас - вернуть перевернутый список (который можно повторять) вместо итератора списка. Затем вы можете использовать сокращение для синтаксиса цикла.

Хакерский путь описан в вопросе , упомянутом ранее , с использованием класса Adapter, который переносит Iterator в Iterable и возвращает Iterator при первом вызове iterator(). Посмотрите на код адаптера, это довольно просто. Он служит цели, но поскольку итератор может быть использован только один раз, он является недопустимым Iterable (он не сможет создать второй итератор).

Разница в поведении ключа проявляется при его двойном использовании:

for(Object a : foo) { }
for(Object a : foo) { }

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

0 голосов
/ 29 января 2010

Как указано выше, вам нужно ссылаться на Iterable (или массив) в блоке for-each. Например, когда вы делаете следующее:

Collection<String> coll = ...;
for (String str : coll) {
    ...
}

Следующее действительно происходит:

Collection<String> coll = ...;
for (Iterator<String> iter = coll.iterator(); iter.hasNext(); ) {
    String str = iter.next();
}

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

class FGH {
  public static void main(String[] args) {
     List list = new ArrayList();
     list.add("1"); list.add("2"); list.add("3");
     while (Object obj : Collections.reverse(list)){
       System.out.println(obj);
     }
  }
}
0 голосов
/ 29 января 2010

Вы бы использовали расширенный цикл for следующим образом:

for (Object o : list)
...