Дозвольте навести кілька прикладів з деякими альтернативами, щоб цього не було 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
якщо ми викликаємо метод видалення. Як такий, я б сказав, що цей підхід менш безпечний, ніж інші, якщо ми не можемо гарантувати підтримку ітератора для видалення елементів.