Удалить два объекта одновременно из ArrayList, не вызывая исключения ConcurrentModificationException?

У меня есть следующий код, который генерирует исключение ConcurrentModificationException. Может кто-нибудь объяснить, почему это происходит?

public void foo(ArrayList<Bet> bets)
   Iterator it1 = bets.iterator();
   while(it1.hasNext())
      Bet b1 = (Bet) bets.next()
      Iterator it2 = bets.iterator();
      while(it2.hasNext())
         if(bet1.equals(bet2))
             it2.remove();
             it1.remove();   //ConcurrentModificationException thrown here

Это тот случай, когда я могу вызывать iterator.remove() только один раз для каждого вызова iterator.next(), и этот вызов remove дважды перед следующим вызовом iterator.next() вызывает это?

Любая помощь будет большим спасибо.


person fulhamHead    schedule 11.10.2014    source источник


Ответы (3)


Вам нужно собрать все удаления в Set и удалить их после завершения всех итераций.

public void foo(ArrayList<Bet> bets) {
    Set<Bet> remove = new HashSet<Bet>();
    for ( Bet bet1 : bets ) {
        for ( Bet bet2 : bets ) {
            // Not the same but equal.
            if ( bet1 != bet2 && bet1.equals(bet2)) {
                remove.add(bet1);
                remove.add(bet2);
            }
        }
    }
    bets.removeAll(remove);
}
person OldCurmudgeon    schedule 11.10.2014
comment
Хотя это старый ответ, но будьте осторожны при использовании removeAll - он имеет экспоненциальное время выполнения (макс.) в зависимости от количества удаляемых элементов. - person user2780757; 10.12.2018
comment
RemoveAll стремится к O (n ^ 2) из-за многократного использования метода contains. - person user2780757; 13.12.2018
comment
@user2780757 user2780757 Это далеко O(e^n) - person OldCurmudgeon; 13.12.2018
comment
О, лол, я вижу, извиняюсь за это. Пока кто-то понимает, я в порядке :-) - person user2780757; 14.12.2018

Это тот случай, когда я могу вызывать iterator.remove() только один раз для каждого вызова iterator.next(), и этот вызов remove дважды перед следующим вызовом iterator.next() вызывает это?

Если вы позвоните remove() без next(), вы получите IllegalStateException

Вы получаете ConcurrentModificationException, потому что используете 2 итератора в одном и том же ArrayList, и оба они удаляют элементы из списка.

Если вы хотите удалить дубликаты из списка, используйте код @OldCurmudgeon или только эти 2 строки:

bets.clear();
bets.addAll(new LinkedHashSet(bets));
person ponomandr    schedule 11.10.2014

Когда элементы удаляются из ArrayList в Java с помощью удаления (int i) (т. е. с использованием индекса) или удаления (объект o), пробел, созданный удалением элемента, должен быть заполнен в базовом массиве. Это делается путем сдвига любых последующих элементов влево (вычитает один из их индексов). Для этого используется метод System.arrayCopy. Процесс также можно назвать перетасовкой.

System.arraycopy(elementData, index+1, elementData, index, numMoved);

Здесь index+1 — исходная позиция, а index — конечная позиция. Поскольку элемент в позиции index удаляется, поэтому элементы, начинающиеся с index+1, копируются в место назначения, начиная с index.

Вы выполняете эту операцию в цикле, Arraylist становится трудно поддерживать определенное состояние в данный момент, и, следовательно, некоторые изменения могут быть потеряны, чтобы избежать этого, у нас есть исключение Concurrentmodification.

person nik_7    schedule 06.12.2019