Mam ArrayList, który chcę iterować. Podczas iteracji po nim muszę jednocześnie usuwać elementy. Oczywiście rzuca tojava.util.ConcurrentModificationException
.
Jaka jest najlepsza praktyka radzenia sobie z tym problemem? Czy powinienem najpierw sklonować listę?
Usuwam elementy nie z samej pętli, ale z innej części kodu.
Mój kod wygląda następująco:
public class Test() {
private ArrayList<A> abc = new ArrayList<A>();
public void doStuff() {
for (A a : abc)
a.doSomething();
}
public void removeA(A a) {
abc.remove(a);
}
}
a.doSomething
może zadzwonić Test.removeA()
;
Odpowiedzi:
Dwie opcje:
originalList.removeAll(valuesToRemove)
na końcuremove()
metody na samym iteratorze. Pamiętaj, że oznacza to, że nie możesz używać rozszerzonej pętli for.Jako przykład drugiej opcji, usunięcie z listy ciągów o długości większej niż 5:
źródło
Z JavaDocs z ArrayList
źródło
Próbujesz usunąć wartość z listy w zaawansowanej pętli for, co nie jest możliwe, nawet jeśli zastosujesz jakąś sztuczkę (co zrobiłeś w kodzie). Lepszym sposobem jest kodowanie poziomu iteratora, zgodnie z zaleceniami innych tutaj.
Zastanawiam się, jak ludzie nie sugerowali tradycyjnego podejścia do pętli.
To też działa.
źródło
Naprawdę powinieneś po prostu powtórzyć tablicę w tradycyjny sposób
Za każdym razem, gdy usuniesz element z listy, elementy po nim zostaną przesunięte do przodu. Dopóki nie zmienisz elementów innych niż iteracyjny, poniższy kod powinien działać.
źródło
W Javie 8 możesz użyć interfejsu Collection i zrobić to, wywołując metodę removeIf:
Więcej informacji można znaleźć tutaj
źródło
Wykonaj pętlę w normalny sposób
java.util.ConcurrentModificationException
jest to błąd związany z dostępnymi elementami.Więc spróbuj:
źródło
java.util.ConcurrentModificationException
, nie usuwając niczego z listy. Zdradliwy. :) Naprawdę nie można nazwać tego „normalnym sposobem” w celu iteracji listy.Podczas iteracji listy można usunąć element. Zobaczmy poniżej moje przykłady,
Mam powyższe nazwy listy Array. I chcę usunąć nazwę „def” z powyższej listy,
Powyższy kod zgłasza ConcurrentModificationException wyjątek ponieważ modyfikujesz listę podczas iteracji.
Tak więc, aby usunąć nazwę „def” z Arraylist, postępując w ten sposób,
Powyższy kod, za pomocą iteratora możemy usunąć nazwę „def” z Arraylist i spróbować wydrukować tablicę, zobaczysz poniższe wyjście.
Dane wyjściowe: [abc, ghi, xyz]
źródło
Jedną z opcji jest zmodyfikowanie
removeA
metody w tym celu -Ale oznaczałoby to Twój
doSomething()
powinien być w stanie przekazaćiterator
doremove
metody. Niezbyt dobry pomysł.Czy możesz to zrobić w podejściu dwuetapowym: w pierwszej pętli, gdy iterujesz listę, zamiast usuwać wybrane elementy, zaznacz je jako do usunięcia . W tym celu możesz po prostu skopiować te elementy (płytka kopia) do innego
List
.Następnie, po zakończeniu iteracji, po prostu zrób
removeAll
z pierwszej listy wszystkie elementy na drugiej liście.źródło
Oto przykład, w którym używam innej listy, aby dodać obiekty do usunięcia, a następnie używam stream.foreach, aby usunąć elementy z oryginalnej listy:
źródło
Zamiast używać Dla każdej pętli użyj normal dla pętli. na przykład poniższy kod usuwa wszystkie elementy z listy tablic bez podania wyjątku java.util.ConcurrentModificationExod. Możesz zmodyfikować warunek w pętli zgodnie z przypadkiem użycia.
źródło
Zrób coś takiego prostego:
źródło
Alternatywne rozwiązanie Java 8 wykorzystujące strumień:
W Javie 7 możesz zamiast tego używać Guava:
Zauważ, że przykład Guawy daje niezmienną listę, która może, ale nie musi być tym, czego chcesz.
źródło
Możesz także użyć CopyOnWriteArrayList zamiast ArrayList. Jest to najnowsze zalecane podejście od JDK 1.5 i nowsze.
źródło
W moim przypadku zaakceptowana odpowiedź nie działa, zatrzymuje wyjątek, ale powoduje pewną niespójność na mojej liście. Poniższe rozwiązanie doskonale dla mnie działa.
W tym kodzie dodałem elementy do usunięcia, na innej liście, a następnie zastosowałem
list.removeAll
metodę usunięcia wszystkich wymaganych elementów.źródło
„Czy powinienem najpierw sklonować listę?”
To będzie najłatwiejsze rozwiązanie, usuń z klonu i skopiuj klon z powrotem po usunięciu.
Przykład z mojej gry Rummikub:
źródło
stones = (...) clone.clone();
jest to zbyteczne. Czy niestones = clone;
zrobiłby tego samego?stones
. W ten sposób nie potrzebujesz nawetclone
zmiennej:for (Stone stone : (ArrayList<Stone>) stones.clone()) {...
Jeśli Twoim celem jest usunięcie wszystkich elementów z listy, możesz iterować każdy element, a następnie wywołać:
źródło
Przyjeżdżam późno, wiem, ale odpowiadam na to, ponieważ uważam, że to rozwiązanie jest proste i eleganckie:
Wszystko to służy do aktualizacji z jednej listy do drugiej i możesz zrobić wszystko z tylko jednej listy, a przy aktualizacji metody sprawdzasz obie listy i możesz wymazać lub dodać elementy między listami. Oznacza to, że obie listy zawsze mają ten sam rozmiar
źródło
Użyj Iteratora zamiast listy macierzy
Niech zestaw zostanie przekonwertowany na iterator z dopasowaniem typu
I przejdź do następnego elementu i usuń
Przejście do następnego jest tutaj ważne, ponieważ powinien zająć indeks, aby usunąć element.
źródło
A co z
źródło
Po prostu dodaj przerwę po instrukcji ArrayList.remove (A)
źródło