AFAIK, есть два подхода:
- Перебрать копию коллекции
- Используйте итератор фактической коллекции
Например,
List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
// modify actual fooList
}
и
Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
// modify actual fooList using itr.remove()
}
Есть ли причины предпочитать один подход другому (например, предпочтение первого подхода по простой причине читабельности)?
java
collections
iteration
user1329572
источник
источник
while
у меня были другие правила определенияfor
fooList
есть переменная экземпляра, и вы вызываете метод во время цикла, который в итоге вызывает другой метод в том же классе, что иfooList.remove(obj)
. Видели это случилось. В этом случае копирование списка является наиболее безопасным.Ответы:
Позвольте мне привести несколько примеров с некоторыми альтернативами, чтобы избежать
ConcurrentModificationException
.Предположим, у нас есть следующая коллекция книг
Собрать и удалить
Первый метод состоит в том, чтобы собрать все объекты, которые мы хотим удалить (например, используя расширенный цикл for), и после того, как мы закончим итерацию, мы удалим все найденные объекты.
Это предполагает, что операция, которую вы хотите сделать, это «удалить».
Если вы хотите «добавить» этот подход, он также будет работать, но я предполагаю, что вы переберите другую коллекцию, чтобы определить, какие элементы вы хотите добавить во вторую коллекцию, а затем выпустите
addAll
метод в конце.Использование ListIterator
Если вы работаете со списками, другой метод заключается в использовании
ListIterator
элемента, который поддерживает удаление и добавление элементов во время самой итерации.Опять же, я использовал метод «удалить» в приведенном выше примере, что, по-видимому, подразумевает ваш вопрос, но вы также можете использовать его
add
метод для добавления новых элементов во время итерации.Используя JDK> = 8
Для тех, кто работает с Java 8 или более поздними версиями, есть несколько других методов, которые вы можете использовать, чтобы воспользоваться этим.
Вы можете использовать новый
removeIf
метод вCollection
базовом классе:Или используйте новый потоковый API:
В последнем случае, чтобы отфильтровать элементы из коллекции, вы переназначаете исходную ссылку на отфильтрованную коллекцию (т. Е.
books = filtered
) Или используете отфильтрованную коллекциюremoveAll
найденным элементам из исходной коллекции (тbooks.removeAll(filtered)
. Е. ).Использовать подсписок или подмножество
Есть и другие альтернативы. Если список отсортирован, и вы хотите удалить последовательные элементы, вы можете создать подсписок, а затем очистить его:
Поскольку подсписок поддерживается исходным списком, это будет эффективным способом удаления этого подколлекции элементов.
Нечто подобное может быть достигнуто с помощью сортированных наборов с использованием
NavigableSet.subSet
метода или любого из предложенных там методов нарезки.Соображения:
Какой метод вы используете, может зависеть от того, что вы собираетесь делать
removeAl
техника работают с любой коллекцией (Коллекция, Список, Набор и т. Д.).ListIterator
Метод работает , очевидно , только со списками, при условии , что их данныеListIterator
предложения по реализации поддержки для добавления и удаления операций.Iterator
Подход будет работать с любым типом коллекции, но он поддерживает только операцию УДАЛИТЬ.ListIterator
/Iterator
подхода очевидным преимуществом является отсутствие необходимости что-либо копировать, поскольку мы удаляем по мере итерации. Итак, это очень эффективно.removeAll
подойти недостаток в том , что мы должны итерация дважды. Сначала мы выполняем итерацию в цикле foor, ища объект, который соответствует нашим критериям удаления, и как только мы его найдем, мы просим удалить его из исходной коллекции, что подразумевает вторую итерационную работу для поиска этого элемента, чтобы убери это.Iterator
интерфейса помечен как «необязательный» в Javadocs, что означает, что могут бытьIterator
реализации, которые выдают,UnsupportedOperationException
если мы вызываем метод удаления. Таким образом, я бы сказал, что этот подход менее безопасен, чем другие, если мы не можем гарантировать поддержку итератора для удаления элементов.источник
removeAll(filtered)
.removeIf(b -> b.getIsbn().equals(other))
В Java 8 есть другой подход. Коллекция # removeIf
например:
источник
Первый подход будет работать, но имеет очевидные накладные расходы при копировании списка.
Второй подход не будет работать, потому что многие контейнеры не допускают изменения во время итерации. Это включает в себя
ArrayList
.Если единственной модификацией является удаление текущего элемента, вы можете заставить работать второй подход с помощью
itr.remove()
(то есть использовать метод итератора , аremove()
не контейнера ). Это был бы мой предпочтительный метод для итераторов, которые поддерживаютremove()
.источник
Iterator
интерфейса помечен как необязательный в Javadocs, что означает, что могут быть реализации Iterator, которые могут выдаватьUnsupportedOperationException
. Таким образом, я бы сказал, что этот подход менее безопасен, чем первый. В зависимости от реализаций, предназначенных для использования, первый подход может быть более подходящим.remove()
на саму оригинальную коллекцию также можно добавитьUnsupportedOperationException
: docs.oracle.com/javase/7/docs/api/java/util/… . К сожалению, интерфейсы контейнеров Java определены как крайне ненадежные (если честно, победить точку интерфейса). Если вы не знаете точную реализацию, которая будет использоваться во время выполнения, лучше сделать что-то неизменным - например, используйте Java 8+ Streams API, чтобы отфильтровать элементы и собрать их в новый контейнер, затем полностью заменить старый на него.Только второй подход будет работать. Вы можете изменить коллекцию во время итерации, используя
iterator.remove()
только. Все остальные попытки вызовутConcurrentModificationException
.источник
Фаворит Старого Таймера (он все еще работает):
Преимущества:
источник
Вы не можете сделать второе, потому что даже если вы используете
remove()
метод Iterator , вы получите исключение .Лично я предпочел бы первый для всех
Collection
случаев, несмотря на то, что при создании новогоCollection
я случайно услышал , что он менее подвержен ошибкам при редактировании другими разработчиками. В некоторых реализациях Collection поддерживается Iteratorremove()
, в других - нет. Вы можете прочитать больше в документации по Iterator .Третий вариант - создать новый
Collection
, выполнить итерацию по оригиналу и добавить все элементы первого,Collection
второгоCollection
, которые не подлежат удалению. В зависимости от размераCollection
и количества удалений это может значительно сэкономить память по сравнению с первым подходом.источник
Я бы выбрал второе, так как вам не нужно делать копию памяти, и итератор работает быстрее. Таким образом, вы экономите память и время.
источник
почему не это?
И если это карта, а не список, вы можете использовать keyset ()
источник
get(i)
вы должны посещать все узлы, пока не достигнетеi
.Foo.remove(i);
вас следует сделатьi--;
?