Ви можете робити те, що хочете, якщо використовуєте об’єкт-ітератор для перегляду елементів у вашому наборі. Ви можете видалити їх на ходу і це нормально. Однак видалення їх, перебуваючи в циклі for (будь-який "стандартний", для кожного виду), призведе до проблем:
Set<Integer> set = new TreeSet<Integer>();
set.add(1);
set.add(2);
set.add(3);
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()) {
Integer setElement = iterator.next();
if(setElement==2) {
iterator.remove();
}
}
for(Integer setElement:set) {
if(setElement==2) {
set.remove(setElement);
}
}
Відповідно до коментаря @ mrgloom, ось докладніше про те, чому описаний вище "поганий" спосіб, ну ... поганий:
Не вдаючись у занадто багато подробиць про те, як Java реалізує це, на високому рівні, ми можемо сказати, що "поганий" спосіб є поганим, оскільки він чітко обумовлений як такий у документах Java:
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
передбачити, серед іншого, що (наголос на моєму):
" Наприклад, загалом неприпустимо, щоб один потік модифікував Колекцію, тоді як інший потік здійснює ітерацію над нею. Загалом, за цих обставин результати ітерації не визначені. Деякі реалізації Ітератора (включаючи реалізацію всієї колекції загального призначення) реалізації, передбачені JRE), можуть обрати цей виняток, якщо така поведінка виявлена "(...)
" Зауважте, що цей виняток не завжди вказує на те, що об'єкт одночасно модифікувався іншим потоком. Якщо один потік видає послідовність викликів методів, що порушує контракт об'єкта, об'єкт може видавати цей виняток. Наприклад, якщо потік модифікує колекцію безпосередньо під час ітерації над колекцією за допомогою швидкого ітератора, ітератор видасть цей виняток. "
Для більш детальної деталізації: об’єкт, який можна використовувати у циклі forEach, повинен реалізувати інтерфейс „java.lang.Iterable” ( тут javadoc ). Це створює Ітератор (за допомогою методу "Ітератор", знайденого в цьому інтерфейсі), який створюється за потреби на вимогу і міститиме внутрішнє посилання на об'єкт Iterable, з якого він був створений. Однак, коли об'єкт Iterable використовується в циклі forEach, екземпляр цього ітератора прихований для користувача (ви не можете отримати до нього жодним чином доступ).
Це в поєднанні з тим фактом, що ітератор є досить вигідним, тобто для того, щоб робити свою магію і мати послідовні відповіді на свої методи "next" і "hasNext", йому потрібно, щоб об'єкт підтримки не змінювався чимось іншим, ніж сам ітератор поки він повторюється, робить це таким чином, що він видасть виняток, як тільки виявить, що щось змінилося в об'єкті, що підтримує, під час ітерації над ним.
Java називає цю "швидкодіючу" ітерацію: тобто є деякі дії, як правило, ті, що модифікують екземпляр Iterable (тоді як ітератор ітерації над ним). Частина "відмова" поняття "швидко спрацьовувати" відноситься до здатності Ітератора виявляти, коли відбуваються такі дії "відмови". "Швидка" частина "швидкого відмови" (і, яку, на мою думку, слід назвати "найвищою швидкістю"), припиняє ітерацію через ConcurrentModificationException, як тільки вона може виявити, що дія "відмови" має трапиться.