Допустим, у меня есть некий ArrayList
.
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++)
list.add((int) (Math.random() * 20));
И я хочу из него удалить все числа больше 10.
"Правильно" сделать это можно через итератор, получив гарантированный результат.
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext(); )
if (iterator.next() > 10)
iterator.remove();
Но на мое удивление, корректно работает и вариант:
for (Integer i : list)
if (i > 10)
list.remove(i);
Несмотря на то, что везде говорится, что любая попытка удаления из коллекции в цикле без использования итератора приводит к ConcurrentModificationException
.
И, действительно, если будем удалять безусловно, получим как раз ConcurrentModificationException
for (Integer i : list)
list.remove(i);
Собственно, может кто-нибудь объяснить магию или дать наводку, где можно почитать на эту тему?
Видимо вам сказочно повезло:
Давайте рассмотрим работу на более коротком примере.
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(22);
list.add(3);
Мы добавили три числа. Итак, как работает foreach
Проверяет на наличие следующего элемента hasNext()
.
public boolean hasNext() {
return cursor != size(); // cursor is zero initially.
}
Если возвращается true
, то берет следующий элемент с помощьюnext()
.
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {
// Initially modCount = expectedModCount (our case 5)
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Далее повторяются шаги 2 и 3 пока hasNext()
не вернет false
.
Если удалить элемент из списка, то его размер уменьшится и modCount
увеличится.
Если удалить элемент во время итерации, то будет выброшено ConcurrentModificationException
исключение на строке
modCount != expectedModCount
.
Но что происходит, если удаляется предпоследний элемент?
> cursor = 0 size = 3 --> hasNext() успешно и next() тоже без эксепшена
> cursor = 1 size = 3 --> hasNext() успешно и next() тоже без эксепшена
Когда мы удалим значение 22, то размер уменьшится до 2.
> cursor = 2 size = 2 --> hasNext() не успешно и next() пропускается.
В других же случаях будет выброшено ConcurrentModificationException
из-за modCount != expectedModCount
.
А в этом единичном случае проверка пройдет на ура..
Вот магия....Или баг....
Да нечего читать на эту тему. В документации ясно сказано, что для удаления элeментов из коллекции нужно использовать итератор.
Тот факт, что в вашем примере вы смогли удалить числа больше 10 обусловлен скорее всего тeм, что там не было таких чисел или было только одно - предпоследнее.
Хотя реалиция foreach
основана на использовании итератора, но если вы представите что внутри foreach
обыкновенный for (int i = 0; i < 10; i++)
то я думаю будет очевидно почему нельзя удалять элементы из массива пока вы его обходите в цикле.
А вообще, используйте Java 8, и забудьте о проблемах :)
List<Integer> list = new ArrayList();
list.removeIf(e -> e > 10);
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Нужно чтобы изображение которое загружалось jLable в приняло размеры такие как сам компонент jLabel Вот код, что здесь нужно добавить?
Имеется viewpaget, он имеет 3 фрагментаНа центральном фрагменте (2) у нас есть горизонтальный кастомный recycler view с карточками
Как можно открыть 2Gis из своего приложения и сразу передать адреса(начальная точка и конечная) чтобы построить маршрут?