Как сделать копию итератора на Java?

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

проверка коммутативна, поэтому, чтобы не повторять ее дважды, мы бы сделали это на C++:

for (list<Object>::iterator it0 = list.begin(); it0 != list.end(); ++it0)
{
    for (list<Object>::iterator it1 = it0; it1 != list.end(); ++it1)
    {
        Test(*it0, *it1);
    }
}

ключевым битом здесь является copy

it1 = it0

Как бы вы написали это на Java?

4 ответов


вы не можете копировать итераторы Java, поэтому вам придется делать это без них:

for(int i=0; i<list.size(); i++){
    for(int j=i; j<list.size(); j++){
        Test(list.get(i), list.get(j));
    }
}

вы можете сделать это с помощью ListIterator:

for(ListIterator<O> outer = list.listIterator(); outer.hasNext() ; ) {
    O oVal = outer.next();
    for(ListIterator<O> inner = list.listIterator(outer.nextIndex()); inner.hasNext(); ) {
         Test(oVal, inner.next());
    }
}

для связанного списка (который имеет медленный доступ к индексу)list.listIterator(index) все равно нужно перебираться в нужное место. Но таким образом, это только O(n2) (и вы не можете получить лучше, чем это) вместо O(n3), как индекс-доступ в других ответах. (Вы можете быть еще быстрее, если сначала скопируете свой список в массив, но здесь это только постоянный фактор.)

конечно, если вам обычно нужен доступ на основе индекса (или этот итератор-клонирование), вам лучше использовать список на основе массива (или пользовательский список, итератор которого поддерживает клонирование).


для связанного списка(который имеет медленный доступ к индексу), я думаю, что есть способ сделать это без замедления o (n2), о котором упоминал Пауло. Пока вы не заботитесь о порядке посещения списка, вы можете запустить внешний цикл из последнего элемента и выполнить итерацию назад, а также запустить внутренний цикл из первого элемента и выполнить итерацию вперед, пока два итератора не встретятся. См.iterRevIterator в коде ниже. Вызов list.listIterator(list.size()) быстро, потому что list это LinkedList, т. е. двунаправленный список, и доступ к последнему элементу не требует итерации по списку. Разница не так уж велика...

iterIndex: 384.59ms sum=29656666
iterIterator: 1.91ms sum=29656666
iterRevIterator: 1.55ms sum=29656666

но все равно значительные.

import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;


public class TestIter {

    public static int iterIndex(List<Integer> list) {
        int sum = 0;
        for(int i = 0; i < list.size(); ++i) {
            for(int j = i+1; j < list.size(); ++j) {
                sum += list.get(i) * list.get(j);
            }
        }
        return sum;
    }

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

    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;
    }

    public static void main(String[] args) {
        int size = 1000;
        int rep = 100;
        int sum = 0;
        // List<Integer> list = new ArrayList<Integer>();
        List<Integer> list = new LinkedList<Integer>();
        for (int i = 0; i < size; ++i) {
            list.add(i);
        }

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < rep; ++i) {
            sum = iterIndex(list);
        }
        System.out.println("iterIndex: " + (float)(System.currentTimeMillis() - startTime)/rep + "ms sum=" + sum);

        startTime = System.currentTimeMillis();
        for (int i = 0; i < rep; ++i) {
            sum = iterIterator(list);
        }
        System.out.println("iterIterator: " + (float)(System.currentTimeMillis() - startTime)/rep + "ms sum=" + sum);

        startTime = System.currentTimeMillis();
        for (int i = 0; i < rep; ++i) {
            sum = iterRevIterator(list);
        }
        System.out.println("iterRevIterator: " + (float)(System.currentTimeMillis() - startTime)/rep + "ms sum=" + sum);

    }

}

for(int i = 0; i < list.size(); i++) {
  for(int j = i; j < list.size(); j++){
    Test(list.get(i), list.get(j));
  }
}