Jak uniknąć "ConcurrentModificationException" podczas usuwania elementów z 'ArrayList' podczas iteracji? [duplikat]
To pytanie ma już odpowiedź tutaj:
- iteracja poprzez kolekcję, unikanie ConcurrentModificationException podczas usuwania w pętli 24 odpowiedzi
Próbuję usunąć niektóre elementy z ArrayList
podczas iteracji w ten sposób:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
Oczywiście, dostaję ConcurrentModificationException
przy próbie usunięcia elementów z listy w tym samym czasie, gdy iteracja myArrayList
. Czy istnieje jakieś proste rozwiązanie tego problemu?
10 answers
Użyj Iterator
i zadzwoń remove()
:
Iterator<String> iter = myArrayList.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 16:29:00
Jako alternatywę dla odpowiedzi innych zawsze robiłem coś takiego:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
Pozwoli to uniknąć bezpośredniego kontaktu z iteratorem, ale wymaga innej listy. Zawsze wolałem tę trasę z jakiegokolwiek powodu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 04:19:12
Java 8 użytkownik może to zrobić: list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
Usunie elementy z listy, dla których niektóre elementy są spełnione
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 12:11:58
Musisz użyć metody remove () iteratora, co oznacza brak enhanced for loop:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-16 20:54:48
Nie, Nie, Nie!
W zadaniach z pojedynczym wątkiem nie trzeba używać iteratora, ponadto CopyOnWriteArrayList (ze względu na hit wydajności).
Rozwiązanie jest znacznie prostsze: spróbuj użyć canonical for loop zamiast for-each loop .
Według właścicieli praw autorskich Javy (kilka lat temu Sun, obecnie Oracle) for-each loop guide , używa iteratora do przechodzenia przez kolekcję i po prostu ukrywa ją, aby Kod wyglądał lepiej. Ale, niestety, jak widać, wyprodukował więcej problemy niż zyski, w przeciwnym razie ten temat nie powstałby.
Na przykład ten kod doprowadzi do Javy.util.ConcurrentModificationException podczas wprowadzania kolejnej iteracji na zmodyfikowanej ArrayList:
// process collection
for (SomeClass currElement: testList) {
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
}
}
Ale następujący kod działa dobrze:
// process collection
for (int i = 0; i < testList.size(); i++) {
SomeClass currElement = testList.get(i);
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
i--; //to avoid skipping of shifted element
}
}
Więc spróbuj użyć metody indeksowania do iteracji nad zbiorami i unikaj pętli for-each, ponieważ nie są one równoważne! For-każda pętla korzysta z iteratorów wewnętrznych, które sprawdzają modyfikację kolekcji i rzucają Wyjątek ConcurrentModificationException. Aby to potwierdzić, przyjrzyj się bliżej wydrukowanemu śladowi stosu w pierwszym przykładzie, który opublikowałem:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at TestFail.main(TestFail.java:43)
Dla wielowątkowości użyj odpowiednich podejść wielozadaniowych (takich jak zsynchronizowane słowo kluczowe).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-24 01:05:18
Podczas gdy inne sugerowane rozwiązania działają, jeśli naprawdę chcesz, aby rozwiązanie było bezpieczne dla wątku, powinieneś zastąpić ArrayList CopyOnWriteArrayList
//List<String> s = new ArrayList<>(); //Will throw exception
List<String> s = new CopyOnWriteArrayList<>();
s.add("B");
Iterator<String> it = s.iterator();
s.add("A");
//Below removes only "B" from List
while (it.hasNext()) {
s.remove(it.next());
}
System.out.println(s);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 16:55:51
Jedną z alternatywnych metod jest konwersja List
na array
, iteracja ich i usuwanie ich bezpośrednio z List
w oparciu o Twoją logikę.
List<String> myList = new ArrayList<String>(); // You can use either list or set
myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");
Object[] obj = myList.toArray();
for(Object o:obj) {
if(condition)
myList.remove(o.toString());
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-21 12:50:20
Jeśli chcesz zmodyfikować swoją listę podczas przechodzenia, musisz użyć Iterator
. Następnie możesz użyć iterator.remove()
, Aby usunąć elementy podczas trawersowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 16:28:25
List myArrayList = Collections.synchronizedList(new ArrayList());
//add your elements
myArrayList.add();
myArrayList.add();
myArrayList.add();
synchronized(myArrayList) {
Iterator i = myArrayList.iterator();
while (i.hasNext()){
Object object = i.next();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 16:48:55
Możesz użyć funkcji iterator remove (), aby usunąć obiekt z bazowego obiektu collection. Ale w takim przypadku możesz usunąć ten sam obiekt, a nie jakikolwiek inny obiekt z listy.
From here
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-22 16:30:43