Что делает метод Iterator.next () внутри оператора for при использовании с java .util.Queue? - PullRequest
0 голосов
/ 07 мая 2020

Например:

public class Test {

    public static void main(String[] args) {
        Queue<String> names = new LinkedList<>(Arrays.asList("First", "Middle", "Last"));
        System.out.println("Queue before the loop: " + names);

        System.out.println("Printing loop...");
        for (Iterator<String> i = names.iterator(); i.hasNext();) {
            String name = i.next();
            System.out.println(name);
        }

        System.out.println("Queue after the loop: " + names);
    }
}

Вывод:

Queue before the loop: [First, Middle, Last]
Printing loop...
First
Middle
Last
Queue after the loop: [First, Middle, Last]

Я знаю, как метод next () проходит по LinkedList . Но когда он вызывается в Queue.iterator () , например, i.next(), что он делает? Как видно из вывода, он не удалил ни одного элемента из очереди. , что, как я думал, будет иметь место, поскольку в очереди есть только remove()/poll().

Ответы [ 2 ]

1 голос
/ 07 мая 2020

Iterator просто используется для итерации по коллекции. В этом случае вы могли бы использовать for-each для того же результата:

for(String name : names){
  System.out.println(name);
}

Однако, исходя из вашего вопроса, я предполагаю, что вы хотите перебрать Queue, выскакивание и печать каждого элемента в порядке FIFO (отсюда и использование вашего LinkedList). В этом случае вы можете просто захотеть l oop names.size() количество раз и вызвать .remove(), чтобы выдавать элемент на каждой итерации, например:

for(int n = names.size(); n > 0; n--){
  String name = names.remove();
  System.out.println(name);
}

Вывод:

Queue before the loop: [First, Middle, Last]
Printing loop...
First
Middle
Last
Queue after the loop: []

Попробуйте онлайн.


РЕДАКТИРОВАТЬ: чтобы немного подробнее объяснить, что происходит с .iterator():

Если мы посмотрим на исходный код Iterator, то увидим интерфейс. Каждая реализация коллекции будет иметь свою собственную индивидуальную реализацию итератора.
Если посмотреть на исходный код Queue, метод iterator() выглядит так:

/**
 * Returns an iterator that iterates over the items in this queue in FIFO order.
 *
 * @return an iterator that iterates over the items in this queue in FIFO order
 */
public Iterator<Item> iterator() {
    return new ListIterator();
}

// an iterator, doesn't implement remove() since it's optional
private class ListIterator implements Iterator<Item> {
    private Node current = first;  // node containing current item

    public boolean hasNext() {
        return current != null;
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }

    public Item next() {
        if (!hasNext()) throw new NoSuchElementException();
        Item item = current.item;
        current = current.next;
        return item;
    }
}

As вы можете видеть, что он сохраняет Node first очереди как свой current, когда ListIterator создается в методе iterator().
В фактическом методе next() он не использует ни remove() ни poll() методов Очереди (ни get() ..), поэтому элементы фактически не всплывают. Вместо этого он просто временно сохраняет текущий узел с Item item = current.item; затем обновляет узел current до следующего с помощью current = current.next; после чего он вернет этот временный item.

0 голосов
/ 10 мая 2020

Поскольку names является объектом LinkedList , а LinkedList не имеет в нем какого-либо метода iterator(), names.iterator() вызовет этот метод in AbstractSequentialList (непосредственный суперкласс LinkedList ).

Однако, отслеживая стек вызовов (можно легко сделать через GUI отладчик любой приличной java IDE) при инициализации i = names.iterator() легко видно, что он вызывает метод listIterator(0) метод здесь . Eventhough AbstractList имеет собственную реализацию listIterator(int index), LinkedList имеет отменяет тот же метод ;

Сегмент LinkedList. java:

package java.util;

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{   
    public ListIterator<E> listIterator(int index) {
            checkPositionIndex(index);
            return new ListItr(index);
    }
    private class ListItr implements ListIterator<E> {
        private Node<E> lastReturned = null;
        private Node<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;
        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);
            nextIndex = index;
        }
        public boolean hasNext() {
            return nextIndex < size;
        }
        public E next() {
            checkForComodification();
            if (!hasNext())
                throw new NoSuchElementException();
            lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.item;
        }
    *
    *   (code contraction...)
    *
        final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
    }
}

Следовательно, очевидно, что names.iterator() вернет объект через return new ListItr(index), который является внутренним классом LinkedList .

Теперь мы можем ясно видеть, что при вызове i.next() он фактически вызывает метод next() во внутреннем классе ListItr . Также он использует переменную класса;

private Node<E> next;

, чтобы отслеживать, куда итератор указывает следующим.

Это важно при рассмотрении производительности усилено для l oop.

Сегмент Oracle документов для hance-for-l oop s:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
    VariableModifiersopt TargetType Identifier = (TargetType) #i.next();
    Statement
}

Как видите, здесь используется метод #i.next(), а с names (в исходном примере вопроса) - это переменная типа Queue , можно в темноте принять #i.next() в расширенном для l oop использовании эта реализация , которая находится в AbstractList , и она сомнительно использует какой-то метод get(int index), следовательно, низкая производительность (бедный, несчастный парень вроде меня, сделал то же самое и застрял в сено- стек кода. LOL).

В связи с этим ложным выводом я задал этот вопрос на этом форуме, и, покопавшись глубже в течение нескольких дней, теперь я вижу, что нет никакого снижения производительности (не что я знаю), когда с использованием расширенного для-l oop для итерации по объекту LinkedList из-за того, что

это объект-итератор (#i) использует переменную Node<E> next, чтобы сохранить ссылку на следующий объект для использования в Enhanced-for-l oop следующая итерация.

...