Дозвольте навести кілька прикладів з деякими альтернативами, щоб цього не було 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")));
Зберіть і видаліть
Перша методика полягає у зборі всіх об'єктів, які ми хочемо видалити (наприклад, використовуючи розширений цикл), і після закінчення ітерації ми видаляємо всі знайдені об’єкти.
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Недоліком у зборі та підході є те, що нам доводиться повторювати двічі. Спочатку ми повторюємо, шукаючи об’єкт, який відповідає нашим критеріям видалення, і, як тільки ми його знайшли, ми просимо видалити його з оригінальної колекції, що означало б повторну роботу ітерації для пошуку цього елемента, щоб видали це.
- Я думаю, що варто згадати, що метод видалення
Iteratorінтерфейсу позначений як "необов'язковий" у Javadocs, це означає, що можуть бути Iteratorреалізації, які кидають, UnsupportedOperationExceptionякщо ми викликаємо метод видалення. Як такий, я б сказав, що цей підхід менш безпечний, ніж інші, якщо ми не можемо гарантувати підтримку ітератора для видалення елементів.