Позвольте мне привести несколько примеров с некоторыми альтернативами, чтобы избежать ConcurrentModificationException
.
Предположим, у нас есть следующая коллекция книг
List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
Собрать и удалить
Первый метод состоит в том, чтобы собрать все объекты, которые мы хотим удалить (например, используя расширенный цикл for), и после того, как мы закончим итерацию, мы удалим все найденные объекты.
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);
Это предполагает, что операция, которую вы хотите сделать, это «удалить».
Если вы хотите «добавить» этот подход, он также будет работать, но я предполагаю, что вы переберите другую коллекцию, чтобы определить, какие элементы вы хотите добавить во вторую коллекцию, а затем выпустите addAll
метод в конце.
Использование ListIterator
Если вы работаете со списками, другой метод заключается в использовании ListIterator
элемента, который поддерживает удаление и добавление элементов во время самой итерации.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
Опять же, я использовал метод «удалить» в приведенном выше примере, что, по-видимому, подразумевает ваш вопрос, но вы также можете использовать его add
метод для добавления новых элементов во время итерации.
Используя JDK> = 8
Для тех, кто работает с Java 8 или более поздними версиями, есть несколько других методов, которые вы можете использовать, чтобы воспользоваться этим.
Вы можете использовать новый removeIf
метод в Collection
базовом классе:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
Или используйте новый потоковый API:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
В последнем случае, чтобы отфильтровать элементы из коллекции, вы переназначаете исходную ссылку на отфильтрованную коллекцию (т. Е. books = filtered
) Или используете отфильтрованную коллекцию removeAll
найденным элементам из исходной коллекции (т books.removeAll(filtered)
. Е. ).
Использовать подсписок или подмножество
Есть и другие альтернативы. Если список отсортирован, и вы хотите удалить последовательные элементы, вы можете создать подсписок, а затем очистить его:
books.subList(0,5).clear();
Поскольку подсписок поддерживается исходным списком, это будет эффективным способом удаления этого подколлекции элементов.
Нечто подобное может быть достигнуто с помощью сортированных наборов с использованием NavigableSet.subSet
метода или любого из предложенных там методов нарезки.
Соображения:
Какой метод вы используете, может зависеть от того, что вы собираетесь делать
- Коллекционирование и
removeAl
техника работают с любой коллекцией (Коллекция, Список, Набор и т. Д.).
ListIterator
Метод работает , очевидно , только со списками, при условии , что их данные ListIterator
предложения по реализации поддержки для добавления и удаления операций.
Iterator
Подход будет работать с любым типом коллекции, но он поддерживает только операцию УДАЛИТЬ.
- При использовании
ListIterator
/ Iterator
подхода очевидным преимуществом является отсутствие необходимости что-либо копировать, поскольку мы удаляем по мере итерации. Итак, это очень эффективно.
- Пример потоков JDK 8 на самом деле ничего не удалял, но искал нужные элементы, а затем мы заменили исходную ссылку на коллекцию новой и позволили старой собирать мусор. Итак, мы перебираем коллекцию только один раз, и это будет эффективно.
- В Собирать и
removeAll
подойти недостаток в том , что мы должны итерация дважды. Сначала мы выполняем итерацию в цикле foor, ища объект, который соответствует нашим критериям удаления, и как только мы его найдем, мы просим удалить его из исходной коллекции, что подразумевает вторую итерационную работу для поиска этого элемента, чтобы убери это.
- Я думаю, что стоит упомянуть, что метод удаления
Iterator
интерфейса помечен как «необязательный» в Javadocs, что означает, что могут быть Iterator
реализации, которые выдают, UnsupportedOperationException
если мы вызываем метод удаления. Таким образом, я бы сказал, что этот подход менее безопасен, чем другие, если мы не можем гарантировать поддержку итератора для удаления элементов.