Клонировать итератор в Java?

В игре у меня есть список игроков, скажем так:

LinkedList<String> players = new LinkedList<String>();

Я хочу, чтобы каждый игрок взаимодействовал с каждым из других игроков, поэтому я пишу два вложенных цикла:

Iterator<String> i1 = players.iterator();
while (i1.hasNext()) {
    String p1 = i1.next();
    Iterator<String> i2 = players.iterator();
    // But I want to do this: Iterator<String> i2 = i1.clone();
    while (i2.hasNext()) {
        String p2 = i2.next();
        System.out.println("Interact: " + p1 + ", " + p2);
    }
}

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

Итак, что мне делать вместо этого?

Ответы

Ответ 1

Это сделает следующее:

ListIterator<String> i1 = players.listIterator(0);
while (i1.hasNext()) {
    String p1 = i1.next();
    ListIterator<String> i2 = players.listIterator(i1.nextIndex());
    while (i2.hasNext()) {
        String p2 = i2.next();
        System.out.println("Interact: " + p1 + ", " + p2);
    }
}

Он полагается на способность ListIterator начать с данной позиции и также знать ее текущую позицию.

Ответ 2

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

elementN = createIterator(linkedList, N).next();

что было бы противоречивым.

В вашей ситуации я считаю, что наиболее эффективным решением было бы сделать

List<String> tmp = new ArrayList<String>(players);
for (int p1 = 0; p1 < tmp.size(); p1++)
    for (int p2 = p1+1; p2 < tmp.size(); p2++)
        System.out.println("Interact: " + tmp.get(p1) + ", " + tmp.get(p2));

Обратите внимание, однако, что это по-прежнему такая же сложность, как и решение aix; O (n 2), но, вероятно, с меньшим постоянным множителем.

Ответ 3

Для решения, которое позволяет избежать линейных затрат, связанных с listIterator(int) (ответ NPE), см. мой ответ на аналогичный вопрос. Короче говоря, до тех пор, пока вы не заботитесь о порядке посещения списка, вы можете запустить внешний цикл из последнего элемента и повторить его и запустить внутренний цикл из первого элемента и итерации вперед до тех пор, пока два итератора не совпадут, Вызов list.listIterator(list.size()) выполняется быстро, потому что список является LinkedList, т.е. Дважды связанный список и доступ к последнему элементу не требует итерации по списку. См. Пример ниже:

public static int iterRevIterator(List<Integer> list) {
    int sum = 0;
    for(ListIterator<Integer> outer = list.listIterator(list.size()); outer.hasPrevious(); ) {
        Integer oVal = outer.previous();
        for(ListIterator<Integer> inner = list.listIterator(); inner.nextIndex() <= outer.previousIndex(); ) {
            sum += oVal * inner.next();
        }
    }
    return sum;
}