Подтвердить что ты не робот

Почему Итератор определяет операцию remove()?

В С# интерфейс IEnumerator определяет способ перемещения коллекции и просмотра элементов. Я думаю, что это чрезвычайно полезно, потому что если вы передадите метод IEnumerable<T> методу, он не будет изменять исходный источник.

Однако в Java Iterator определяет remove (необязательно!) позволяет удалять элементы. Нет никакого преимущества при передаче Iterable<T> методу, потому что этот метод все еще может изменять исходную коллекцию.

remove необязательность является примером отказался от завещания, но игнорируя это (уже обсуждалось здесь) Меня бы интересовали дизайнерские решения, в которых было предложено событие remove, которое должно быть реализовано на интерфейсе.

Каковы конструктивные решения, которые привели к добавлению remove к Iterator?

Иными словами, что такое конструкторское решение С#, которое явно не имеет remove, определенного на IEnumerator?

4b9b3361

Ответ 1

Iterator способен удалять элементы во время итерации. Вы не можете итерировать коллекцию с помощью итератора и удалять элементы из целевой коллекции с помощью метода remove() этой коллекции. Вы получите ConcurrentModificationException при следующем вызове Iterator.next(), потому что итератор не может знать, как именно была изменена коллекция, и не может знать, как продолжить итерацию.

Когда вы используете remove() итератора, он знает, как была изменена коллекция. Более того, на самом деле вы не можете удалить элемент коллекции, но только текущий. Это упрощает продолжение итерации.

Что касается преимуществ передачи итератора или Iterable: вы всегда можете использовать Collection.unmodifireableSet() или Collection.unmodifireableList(), чтобы предотвратить изменение вашей коллекции.

Ответ 2

Вероятно, это связано с тем, что удаление элементов из коллекции при итерации по ней всегда было причиной ошибок и странного поведения. Из чтения документации было бы предположительно, что Java будет выполняться во время выполнения. Remove() вызывается только один раз для каждого вызова для next(), что заставляет меня думать, что он только что был добавлен, чтобы люди не пытались удалить данные из списка при итерации по нему.

Ответ 3

Бывают ситуации, когда вы хотите удалить элементы с помощью итератора, потому что это самый эффективный способ сделать это. Например, при перемещении связанной структуры данных (например, связанного списка) удаление с использованием итератора является операцией O(1)... по сравнению с O(N) с помощью операций List.remove().

И, конечно, многие коллекции разработаны таким образом, что изменение коллекции во время сбора любым другим способом, кроме Iterator.remove(), приведет к ConcurrentModificationException.


Если у вас есть ситуация, когда вы не хотите разрешать модификацию с помощью итератора коллекции, обертывая его с помощью Collection.unmodifiableXxxx и используя его, то итератор будет иметь желаемый эффект. В качестве альтернативы, я считаю, что Apache Commons предоставляет простую немодифицируемую оболочку итератора.


Кстати, IEnumerable страдает от того же "запаха", что и Iterator. Взгляните на метод reset(). Мне также было любопытно, как класс С# LinkedList имеет дело с проблемой удаления O(N). Похоже, что он делает это, подвергая внутренности списка... в виде свойств First и Last, значения которых являются ссылками LinkedListNode. Это нарушает другой принцип проектирования... и является (ИМО) гораздо более опасным, чем Iterator.remove().

Ответ 4

Это действительно потрясающая особенность Java. Как вы, возможно, знаете, при повторении списка в .NET для удаления элементов (из которых есть несколько вариантов использования) у вас есть только два варианта.

var listToRemove = new List<T>(originalList);
foreach (var item in originalList)
{
    ...
    if (...)
    {
        listToRemove.Add(item)
    }
    ...
}

foreach (var item in listToRemove)
{
    originalList.Remove(item);
}

или

var iterationList = new List<T>(originalList);
for (int i = 0; i < iterationList.Count; i++)
{
    ...
    if (...)
    {
        originalList.RemoveAt(i);
    }
    ...
}

Теперь я предпочитаю второй, но с Java мне не нужно все это, потому что пока я нахожусь на элементе, я могу его удалить, но итерация продолжится! Честно говоря, хотя это может показаться неуместным, это действительно оптимизация во многих отношениях.