Java: почему итераторы не копируются - PullRequest
10 голосов
/ 30 сентября 2010

Я бы подумал, что Iterator.copy() будет довольно удобной функцией. Вы могли бы реализовать фильтры итераторов гораздо лучше.

Например, единственная причина в Googles Java Collection для использования функций filter (и аналогичных) UnmodifiableIterator (что является просто Iterator без remove) состоит в том, что вы не можете реализовать такой фильтр Iterator иначе без возможности скопировать его в какой-то момент. (Действительно, это невозможно с текущим интерфейсом; попробуйте сами.)

Еще одним преимуществом будет то, что вы можете использовать итератор в цикле for-each: поскольку копируемый итератор также автоматически будет повторяться. Смотрите также этот вопрос. В настоящее время основная причина, по которой это не разрешается, заключается в том, что Iterator, в котором реализованы Iterable и Iterator<T> iterator() { return this; }, лишит законной силы итератор. Имея функцию copy, она так же проста, как и Iterator<T> iterator() { return copy(); }, и она не сделает недействительным исходный итератор. Таким образом, больше нет причин не позволять это.

Есть ли причина? Просто чтобы сделать его менее сложным для реализации?

Ответы [ 11 ]

10 голосов
/ 30 сентября 2010

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

5 голосов
/ 05 октября 2010

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

Например, вы можете перебирать байты по мере их потоковой передачи с веб-сервера, и в этом случае будет невозможно сообщить среднему потоку веб-сервера: «С этой позиции я хочу, чтобы вы отправляли мне те же байты дважды, но асинхронно, как я их запрашиваю. "

Существует только один поток, и его нельзя скопировать.

Тот факт, что большинство Iterator s, которые вы обычно видите, превышает Collection, является случайным.

2 голосов
/ 30 сентября 2010

Единственная причина, по которой Google имеет UnmodifiableIterator, заключается в том, чтобы гарантировать неизменность в своих коллекциях. Они следят за тем, чтобы вы не могли изменить внутреннее состояние коллекции.

Не забывайте, что первоначальная идея для итератора состоит в том, что это указатель на текущий элемент во время трансверали, и он управляет следующим / предыдущим трансверсалом (для реверс для двусвязных итераторов) к элементу, следующему / предыдущему это.

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

1 голос
/ 05 сентября 2011

В качестве упрощенного примера того, почему вы хотите скопировать итератор, рассмотрите следующий код, который находит первую пару совпадающих значений в одном массиве.

for(int i=0;i<size;i++)
{
  x = array[i];

  for(int j=i+1;j<size;j++)
  {
    y = array[j];
    if(x == y)
    {
      doSomething();
      break;
    }
}

Обратите внимание на «j = i + 1». Вот где вы столкнетесь с проблемами с итератором. Да ладно, обходные пути довольно распространены в Java, кажется ...

1 голос
/ 05 октября 2010

Я хотел что-то вроде этого, вот что я сделал (основываясь на некоторой работе над Lambdaj).
Основной недостаток заключается в том, что этот Iterator в основном заполнит List всем предполагаемым контентомИтератора, который может быть очень тяжелым в памяти.

Почему я использовал List, потому что иногда Iterator выполняет итерацию в определенном порядке, поэтому "sub- Iterators" должен делать то же самое (и ListIterator действительно помогает мне здесь).

public class IterableIterator<T> implements Iterable<T>, Iterator<T> {
    //The content of the given iterator. Will be filled by its iterators.
    private final List<T> iteratorContent = new ArrayList<T>();
    private final Iterator<T> originalIterator;
    private final Iterator<T> innerIterator;

    public IterableIterator(Iterator<T> originalIterator) {
        this(originalIterator, false);
    }

    public IterableIterator(Iterator<T> originalIterator, boolean cache) {
        if (originalIterator == null) {
            throw new IllegalArgumentException("Parameter can't be null");
        }

        this.originalIterator = originalIterator;
        if (cache) {
            while (originalIterator.hasNext()) {
                iteratorContent.add(originalIterator.next());
            }
        }

        innerIterator = iterator();
    }

    @Override
    public Iterator<T> iterator() {
        return new IteratorIterator();
    }

    @Override
    public boolean hasNext() {
        return innerIterator.hasNext();
    }

    @Override
    public T next() {
        return innerIterator.next();
    }

    @Override
    public void remove() {
        innerIterator.remove();
    }

    private class IteratorIterator implements Iterator<T> {
        private ListIterator<T> innerIterator = iteratorContent.listIterator();

        @Override
        public boolean hasNext() {
            return innerIterator.hasNext() || originalIterator.hasNext();
        }

        @Override
        public T next() {
            if (!innerIterator.hasNext() && originalIterator.hasNext()) {
                T item;
                synchronized (originalIterator) {
                    item = originalIterator.next();
                    iteratorContent.add(item);
                }
                innerIterator = iteratorContent.listIterator(innerIterator.nextIndex());
            }
            if (innerIterator.hasNext()) {
                try {
                    return innerIterator.next();
                } catch (ConcurrentModificationException e) {
                    //Quick and dirty solution if you have a concurrent modification.
                    //It can't happen from the outside, so you can easily suppose that another originalIterator
                    //from this class has been called and had added elements to the list.
                    //Best thing to do, reset the originalIterator to the current position.
                    innerIterator = iteratorContent.listIterator(innerIterator.nextIndex());
                    return innerIterator.next();
                }
            }

            throw new NoSuchElementException();
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
1 голос
/ 30 сентября 2010

Вы всегда можете реализовать свой собственный CopyableIterator, который реализует Iterator. И тогда вы можете сделать

new CopyableItereator(collection);

Класс будет таким

class CopyableIterator implements Iterator{
Iterator iterator;
Collection collection;
int index=0;

public CopyableIterator(Collection collection){
super();
this.collection = collection;
this.iterator = collection.iterator();
}

public CopyableIterator(Collection collection, int index){
super();
this.collection =collection;
this.iterator = collection.iterator();
this.advanceToIndex(iterator,index); //This function just moves the iterator till the index.
this.index=index;
}

//Override the functions of Iterator here returning iterator.function()

@Override
public Object next(){
index++;
return this.iterator.next();
}

public CopyableIterator copy(){
return new CopyableIterator(this.collection,this.index)

}

}

Отказ от ответственности: Это примерно класс. Это не было проверено.

0 голосов
/ 22 апреля 2016

ILMTitan и Christoffer Hammarström подразумевают, но конкретно не утверждают, что копирование потока может быть невозможным, поскольку для него необходимо, чтобы в элементах потока была реализована функция копирования, чтобы сохранить состояние, которое потребуется для копируемого итератора.Поймите, что элементы могут быть изменяемыми (или ссылаться на динамические значения), и они могут ссылаться на другие структуры и семантику, которые требуют настраиваемой функции копирования.

Таким образом, копируемые итераторы не ортогональны копируемым элементам потока, поэтому именно поэтому копируемые итераторывообще невозможны.

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

Другая причина заключается в том, что некоторые низкоуровневые оптимизации могут быть невозможны при компиляции на языке ассемблера.

0 голосов
/ 26 января 2015

Вы не можете возможно скопировать итератор - это в принципе бессмысленно. Для некоторых это очевидно из интерфейса Iterator, но давайте продемонстрируем это на конкретном примере. На самом деле, давайте продемонстрируем на примере около бетона .

bar with pieces of concrete

Это изображение конкретного итератора над бетонной плитой. Итерация в нашем случае означает применение ломика, чтобы отломать кусок штанги. Теперь обратите внимание, что:

  • Панель не является набором элементов (хотя некоторые из них имеют ошибки): мы создаем элементы в процессе итерации.
  • Результат итерации через итератор (из next()) никогда не может быть результатом другой итерации бара. Результат был удален из него.
  • Итерация может привести к разным частям в зависимости от погоды, от силы, которую вы применяете, или, возможно, от какого-то теплового шума (подумайте: случайность).
  • Результат итерации с помощью итератора (next()) никогда не может быть результатом другой итерации столбца - поскольку вероятностное пространство точного результата итерации непрерывно, и ни один конкретный результирующий фрагмент не имеет ненулевого мера вероятности.

Любое из вышеперечисленного должно убедить вас не пытаться "скопировать итератор", это глупо ...

0 голосов
/ 30 сентября 2010

Итераторы были созданы для пошагового прохождения всех объектов коллекции по одному, используя данные, подкрепленные указанной коллекцией.

Iterator<T> почти всегда реализуется с использованием частного внутреннего класса, который может использовать состояниеявляется частью внешнего класса.Таким образом, вы не можете реально изменить поведение Iterator, не написав также свой собственный Collection (или любой другой).

Копирование Итератора может вызвать множество новых проблем, таких как выпадениесинхронизации с резервной коллекцией.

0 голосов
/ 30 сентября 2010

Что именно будет означать копирование Iterator?Ты имеешь в виду, что он должен быть в состоянии создать новый Iterator точно так же, как он сам, кроме как в начале?Это обязанность Iterable ... не имеет смысла дублировать эту функциональность, особенно учитывая состояние итераторов с состоянием ... это просто запутает вещи.

Что бы вы ожидалиесли бы вы написали:

Iterator<Foo> iter = someIterable.iterator();
iter.next();
iter.next();
for (Foo foo : iter) {
  ...
}

Ожидаете ли вы, что цикл for будет перебирать каждый элемент, который будет возвращать итератор, или каждый, кроме первых двух?Вы ожидаете, что итератор будет пустым после завершения цикла for?

...