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;
   }
}
Author: Xolve, 2010-02-26

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.

 16
Author: polygenelubricants,
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

  1. 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).
  2. 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;
}
 7
Author: Lawrence Dol,
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.

 6
Author: JRL,
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

 4
Author: Zorglub,
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.

 1
Author: Stroboskop,
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.
 1
Author: whiskeysierra,
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);
    }
}
 0
Author: fatbone,
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);
    }

}
 0
Author: William Brendel,
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