LinkedList: usuń obiekt
Czy jest to prawidłowy sposób znajdowania i usuwania elementów z LinkedList w Javie przy użyciu for each loop, czy możliwe jest wystąpienie niespójności:
for(ObjectType ob : obList) {
if(ob.getId() == id) {
obList.remove(ob);
break;
}
}
8 answers
Inni wspominali poprawny punkt, że normalnie nie jest tak, jak ty remove
obiekt ze zbioru. Jednak w tym przypadku jest to w porządku, ponieważ break
z pętli raz remove
.
Jeśli chcesz kontynuować iterację po remove
, musisz użyć iteratora. W przeciwnym razie otrzymasz ConcurrentModificationException
, lub w bardziej ogólnym przypadku, nieokreślone zachowanie.
Więc tak, Jeśli break
z foreach
po tobie remove
, będzie dobrze .
Do tych kto mówi, że to się nie powiedzie, ponieważ nie można modyfikować kolekcji w foreach
-- to prawda tylko wtedy, gdy chcesz kontynuować iterację. Tutaj tak nie jest, więc ten skrót jest w porządku.
A ConcurrentModificationException
jest sprawdzane i wyrzucane przez iterator. Tutaj, po remove
(co kwalifikuje się jako jednoczesna modyfikacja), wy break
z pętli. Iterator nawet nie ma szansy go wykryć.
Najlepiej będzie, jeśli dodasz komentarz do break
, Dlaczego jest to absolutnie konieczne, itp, ponieważ Jeśli ten kod zostanie później zmodyfikowany, aby kontynuować iterację po remove
, zakończy się niepowodzeniem .
Potraktowałbym ten idiom podobnie do goto
(a raczej oznaczony break
/continue
): na początku może się to wydawać złe, ale gdy jest mądrze używane, tworzy czystszy kod.
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-02-26 18:31:20
Najlepiej jest użyć iteratora i użyć jego metody remove podczas wyszukiwania obiektu poprzez iterację nad kolekcją w celu jego usunięcia. Dzieje się tak dlatego, że
- zbiorem może być np. lista powiązana (i w Twoim przypadku tak jest), której metoda remove oznacza szukanie obiektu od nowa, której wyszukiwanie może mieć złożoność O(n).
- nie możesz kontynuować iteracji po usunięciu, chyba że użyjesz metody usuwania iteratora. W tej chwili usuwasz pierwsze wystąpienie-w przyszłości może być konieczne usunięcie wszystkich pasujących wystąpień, w takim przypadku należy przepisać pętlę.
Zalecam, z zasady, rezygnację z enhanced for i użycie czegoś takiego zamiast:
for(Iterator<ObjectType> it=obList.iterator(); it.hasNext(); ) {
if(it.next().getId()==id) {
it.remove();
break;
}
}
W ten sposób nie tworzysz założeń dotyczących listy bazowej, które mogą się zmienić w przyszłości.
Porównaj kod, aby usunąć ostatni wpis wywołany przez iterator remove (formatowanie Sun ' s):
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
Przeciwko temu, co remove (Object) must do:
public boolean remove(Object o) {
if (o==null) {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}
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-02-27 19:16:41
Powinieneś użyć iterator.remove()
:
Usuwa z bazowego zbioru ostatni element zwracany przez iterator (operacja opcjonalna). To metodę można wywołać tylko raz na zadzwoń do następnego. Zachowanie iterator jest nieokreślony jeśli zbiór bazowy jest modyfikowany w trakcie iteracji w w inny sposób niż nazywając to metoda.
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-02-26 17:54:32
Edit: rzeczywiście, nie zawiedzie dzięki przerwie. Zobacz odpowiedź polygenelubricant po szczegóły.
Jednak jest to niebezpieczny sposób. Aby jednocześnie iterować i modyfikować kolekcję w Javie, należy użyć obiektu "ListIterator" i użyć własnych metod "add()" I "remove()" iteratora, a nie używać tych, które znajdują się w kolekcji.
Możesz sprawdzić dokument java pod kątem "java.util.Iterator " i " java.util.ListIterator " klasy
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-02-26 18:09:36
Spróbuj czegoś takiego:
Iterator<ObjectType> iter = obList.iterator();
while (iter.hasNext()) {
ObjectType ob = iter.next();
if(ob.getId() == id) {
iter.remove();
break;
}
}
To jedno z ostatnich miejsc, gdzie Iterator nie może być zastąpiony pętlą foreach.
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-02-26 17:52:55
aby uniknąć ConcurrentModifiationException, możesz zrobić:
final Iterator<ObjectType> i = obList.iterator();
while (i.hasNext()) {
if (i.next().getId() == id) {
i.remove();
}
}
Lub
for (int i = 0; i < obList.size(); i++) {
if (obList[i].getId() == id) {
obList.remove(i);
}
}
Wolałbym pierwszą. Obsługa indeksów jest bardziej błędna i iterator może być efektywnie zaimplementowany. I pierwsza sugestia działa z Iterable, podczas gdy druga wymaga listy.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-02-26 18:01:23
Powyższa druga pętla powinna być nieco zmieniona
for (int i = 0; i < obList.size(); ) {
if (obList.get(i).getId() == id) {
obList.remove(i);
continue
}
++i;
}
Lub
for (int i = obList.size() - 1; i >= 0; --i) {
if (obList.get(i).getId() == id) {
obList.remove(i);
}
}
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-07-13 10:18:55
A CopyOnWriteArrayList
może tego szukasz. Podczas wykonywania operacji mutacyjnych wykonywana jest kopia tablicy bazowej. Pozwala to na modyfikację elementów listy wewnątrz pętli for-each. Pamiętaj jednak, że nie jest to lista powiązana i może być dość nieefektywna.
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Main {
public static void main(String[] args) {
List<String> myList = new CopyOnWriteArrayList<String>();
myList.add("a");
myList.add("b");
myList.add("c");
// Will print [a, b, c]
System.out.println(myList);
for (String element : myList) {
if (element.equals("a")) {
myList.remove(element);
}
}
// Will print [b, c]
System.out.println(myList);
}
}
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-07-31 02:16:45