Wywołanie remove W pętli foreach w Javie [duplikat]
To pytanie ma już odpowiedź tutaj:
- iteracja poprzez kolekcję, unikanie ConcurrentModificationException podczas usuwania w pętli 24 odpowiedzi
W Javie, czy jest legalne wywoływanie remove na kolekcji podczas iteracji poprzez kolekcję za pomocą pętli foreach? Na przykład:
List<String> names = ....
for (String name : names) {
// Do something
names.remove(name).
}
Jako dodatek, czy usuwanie przedmiotów jest legalne to jeszcze nie zostało powtórzone? Na przykład,
//Assume that the names list as duplicate entries
List<String> names = ....
for (String name : names) {
// Do something
while (names.remove(name));
}
11 answers
Aby bezpiecznie usunąć kolekcję podczas iteracji, należy użyć iteratora.
Na przykład:
List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String s = i.next(); // must be called before you can call i.remove()
// Do something
i.remove();
}
Iteratory zwracane przez iteratora i listeratora tej klasy metody są Fail-fast: jeśli lista jest strukturalnie zmodyfikowana w dowolnym czasu po utworzeniu iteratora, w jakikolwiek sposób, z wyjątkiem własne metody usuwania lub dodawania iteratora, iterator wyrzuci ConcurrentModificationException. Tak więc, w obliczu równoczesnego modyfikacji, iterator nie działa szybko i czysto, a nie ryzykując arbitralne, niedeterministyczne zachowanie w nieokreślonym czasie w przyszłości.
Być może to, co jest niejasne dla wielu nowicjuszy, to fakt, że iteracja nad listą za pomocą konstruktów for / foreach pośrednio tworzy iterator, który jest koniecznie niedostępny. Informacje te można znaleźć tutaj
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-12-02 11:11:03
Nie chcesz tego robić. Może powodować nieokreślone zachowanie w zależności od kolekcji. Chcesz użyć iteratora bezpośrednio. Chociaż dla każdego konstruktu jest cukrem składniowym i naprawdę używa iteratora, ukrywa go przed Twoim kodem, więc nie możesz uzyskać do niego dostępu Iterator.remove
.
Zachowanie iteratora jest nieokreślony, jeżeli bazowy kolekcja jest modyfikowana podczas iteracja trwa w jakikolwiek sposób inne niż nazywając to metoda.
Zamiast tego napisz swój kod:
List<String> names = ....
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
// Do something
it.remove();
}
Zauważ, że kod wywołuje Iterator.remove
, a nie List.remove
.
Dodatek:
Nawet jeśli usuwasz element, który nie został jeszcze iterowany, nadal nie chcesz modyfikować kolekcji, a następnie używać Iterator
. Może to zmodyfikować zbiór w sposób zaskakujący i wpływający na przyszłe operacje na Iterator
.
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
2009-07-28 20:48:37
Konstrukcja Javy "enhanced for loop" miała nie narazić iteratora na kod, ale jedynym sposobem na bezpieczne usunięcie elementu jest dostęp do iteratora. Więc w tym przypadku musisz to zrobić po staremu:
for(Iterator<String> i = names.iterator(); i.hasNext();) {
String name = i.next();
//Do Something
i.remove();
}
Jeśli w kodzie rzeczywistym pętla enhanced for jest naprawdę tego warta, możesz dodać elementy do kolekcji tymczasowej i wywołać removeAll na liście po pętli.
EDIT (re addendum): nie, Zmiana listy w jakikolwiek sposób poza iteratorem.metoda remove () while iteracja spowoduje problemy. Jedynym sposobem obejścia tego jest użycie CopyOnWriteArrayList, ale tak naprawdę jest to przeznaczone do problemów z współbieżnością.
Najtańszym (jeśli chodzi o linie kodu) sposobem na usunięcie duplikatów jest zrzut listy do LinkedHashSet (a następnie z powrotem do listy, jeśli zajdzie taka potrzeba). Pozwala to zachować kolejność wstawiania podczas usuwania duplikatów.
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-07-18 13:31:40
for (String name : new ArrayList<String>(names)) {
// Do something
names.remove(nameToRemove);
}
Sklonujesz listę names
i powtarzasz przez klon podczas usuwania z oryginalnej listy. Trochę czystsza niż najlepsza odpowiedź.
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
2015-11-22 03:37:30
Nie wiedziałem o iteratorach, jednak oto, co robiłem do dziś, aby usunąć elementy z listy wewnątrz pętli:
List<String> names = ....
for (i=names.size()-1;i>=0;i--) {
// Do something
names.remove(i);
}
To zawsze działa i może być używane w innych językach lub strukturach nie obsługujących iteratorów.
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-02-27 14:53:42
Tak możesz użyć pętli for-each,
Aby to zrobić, musisz zachować oddzielną listę, aby trzymać usuwanie elementów, a następnie usunąć tę listę z listy nazw za pomocą metody removeAll()
,
List<String> names = ....
// introduce a separate list to hold removing items
List<String> toRemove= new ArrayList<String>();
for (String name : names) {
// Do something: perform conditional checks
toRemove.add(name);
}
names.removeAll(toRemove);
// now names list holds expected values
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
2010-12-11 13:00:54
Te, które mówią, że nie można bezpiecznie usunąć elementu z kolekcji poza iteratorem, nie są całkiem poprawne, można to zrobić bezpiecznie używając jednej z współbieżnych kolekcji, takich jak ConcurrentHashMap.
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
2009-07-28 23:25:51
Upewnij się, że to nie zapach kodu. Czy można odwrócić logikę i być "inkluzywnym", a nie "wyłącznym"?
List<String> names = ....
List<String> reducedNames = ....
for (String name : names) {
// Do something
if (conditionToIncludeMet)
reducedNames.add(name);
}
return reducedNames;
Sytuacja, która doprowadziła mnie do tej strony dotyczyła starego kodu, który przechodził przez Listę używając indecies do usuwania elementów z listy. Chciałem go refaktorować, aby użyć stylu foreach.
Przepętał całą listę elementów, aby sprawdzić, do których z nich użytkownik miał uprawnienia dostępu, i usunął te, które nie miały uprawnień z lista.
List<Service> services = ...
for (int i=0; i<services.size(); i++) {
if (!isServicePermitted(user, services.get(i)))
services.remove(i);
}
Aby to odwrócić i nie używać Usuń:
List<Service> services = ...
List<Service> permittedServices = ...
for (Service service:services) {
if (isServicePermitted(user, service))
permittedServices.add(service);
}
return permittedServices;
Kiedy preferowane jest "Usuń"? Jednym z rozważań jest, jeśli Gien dużą listę lub drogie "dodać", w połączeniu z tylko kilka usunięte w porównaniu do wielkości listy. Może być bardziej wydajne, aby zrobić tylko kilka usunięć, a nie wiele dodań. Ale w moim przypadku sytuacja nie zasługiwała na taką optymalizację.
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
2011-11-14 16:57:57
-
Spróbuj tego 2. i zmień warunek na "zimowy" a będziesz się zastanawiał:
public static void main(String[] args) {
Season.add("Frühling");
Season.add("Sommer");
Season.add("Herbst");
Season.add("WINTER");
for (String s : Season) {
if(!s.equals("Sommer")) {
System.out.println(s);
continue;
}
Season.remove("Frühling");
}
}
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
2011-08-30 15:42:18
Lepiej jest użyć iteratora, gdy chcemy usunąć element z listy
Ponieważ kod źródłowy remove To
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
Tak więc, jeśli usuniesz element z listy, lista zostanie zrestrukturyzowana, indeks innego elementu zostanie zmieniony, może to spowodować coś, co chcesz się stało.
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
2012-02-09 08:51:44
Użyj
.remove() Of Interator or
Użyj
CopyOnWriteArrayList
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-10-10 05:41:46