it-swarm.com.de

Löschen von Objekten aus einer ArrayList in Java

Ich muss einige Objekte aus einem ArrayList löschen, wenn sie eine Bedingung erfüllen, und ich frage mich, welcher Weg effizienter sein könnte.

Hier ist die Situation: Ich habe eine Klasse, die ein ArrayList enthält, das einige andere Objekte enthält. Ich muss diesen ArrayList durchlaufen und alle Elemente löschen, die eine bestimmte Bedingung erfüllen. Soweit ich weiß, sind dies meine Löschoptionen:

  1. Erstellen Sie ein neues ArrayList und fügen Sie die Elemente hinzu, die die Bedingung nicht erfüllen. Wechseln Sie nach der Iteration von der alten Arrayliste zur neuen ohne die Elemente.

  2. Erstellen Sie ein neues ArrayList und fügen Sie die Elemente hinzu, die die Bedingung erfüllen. Verwenden Sie nach der Iteration die Methode removeAll(), die das ArrayList mit den zu löschenden Objekten übergibt.

Gibt es eine effizientere Möglichkeit, Objekte aus einem ArrayList zu löschen?

32
Carlos Pastor

Eine andere Möglichkeit: Der Iterator verfügt über eine optionale remove () - Methode, die für ArrayList implementiert ist. Sie können es beim Iterieren verwenden.

Ich weiß allerdings nicht, welche Variante am performantesten ist, man sollte sie messen.

starblue bemerkte, dass die Komplexität nicht gut ist, und das ist wahr (auch für removeAll ()), da ArrayList alle Elemente kopieren muss, wenn in der Mitte ein Element hinzugefügt oder entfernt wird. In diesen Fällen sollte eine LinkedList besser funktionieren. Aber da wir alle Ihre tatsächlichen Anwendungsfälle nicht kennen, ist es das Beste, alle Varianten zu messen, um die beste Lösung zu finden.

16
Mnementh

Sie können rückwärts iterieren und entfernen, während Sie die ArrayList durchlaufen. Dies hat den Vorteil, dass nachfolgende Elemente nicht verschoben werden müssen, und ist einfacher zu programmieren als vorwärts zu bewegen.

47
RichardOD

Die meisten Performanten würden vermutlich die Methode listIterator verwenden und eine umgekehrte Iteration durchführen:

for (ListIterator<E> iter = list.listIterator(list.size()); iter.hasPrevious();){
    if (weWantToDelete(iter.previous()))  iter.remove();
}

Edit: Viel später möchte man vielleicht auch the Java 8 way of remove elements aus einer Liste (oder einer Sammlung!) mit einem Lambda oder einer Methodenreferenz. Ein In-Place filter für Sammlungen, wenn Sie möchten:

list.removeIf(e -> e.isBad() && e.shouldGoAway());

Dies ist wahrscheinlich der beste Weg, um eine Sammlung aufzuräumen. Da die interne Iteration verwendet wird, kann die Auflistungsimplementierung Verknüpfungen verwenden, um sie so schnell wie möglich zu machen (für ArrayLists kann der Kopieraufwand minimiert werden).

12
gustafc

Offensichtlich ist die Nummer 1 der beiden von Ihnen genannten Methoden effizienter, da sie nur einmal durch die Liste gehen muss, während bei Methode 2 die Liste zweimal durchlaufen werden muss (zuerst, um die zu entfernenden Elemente zu finden, und dann, um sie zu entfernen) entferne sie).

Tatsächlich ist das Entfernen einer Liste von Elementen aus einer anderen Liste wahrscheinlich ein Algorithmus, der schlechter als O(n) ist, sodass Methode 2 noch schlechter ist.

Die Iterator-Methode:

List data = ...;

for (Iterator i = data.iterator(); i.hasNext(); ) {
    Object element = i.next();

    if (!(...)) {
        i.remove();
    }
}
5
Jesper

Zuerst würde ich sicherstellen, dass dies wirklich ein Leistungsengpass ist, andernfalls würde ich mich für die Lösung entscheiden, die am saubersten und aussagekräftigsten ist.

Wenn es IS ein Leistungsengpass ist, probieren Sie einfach die verschiedenen Strategien aus und sehen Sie, was am schnellsten ist. Meine Wette ist, eine neue ArrayList zu erstellen und die gewünschten Objekte in diese zu legen und die alte ArrayList zu verwerfen.

4
Buhb
int sizepuede= listaoptionVO.size();
for (int i = 0; i < sizepuede; i++) {
    if(listaoptionVO.get(i).getDescripcionRuc()==null){
        listaoptionVO.remove(listaoptionVO.get(i));
        i--;
        sizepuede--;
     }
}

edit: Einrückung hinzugefügt

1
Raulitoxd

Das Entfernen von Elementen aus einer ArrayList ist mit versteckten Kosten verbunden. Jedes Mal, wenn Sie ein Element löschen, müssen Sie die Elemente verschieben, um das "Loch" zu füllen. Im Durchschnitt werden hierfür N / 2 Zuweisungen für eine Liste mit N Elementen benötigt.

Das Entfernen von M Elementen aus einer ArrayList mit N Elementen ist also im Durchschnitt O(M * N). Eine O(N) Lösung besteht darin, eine neue Liste zu erstellen. Zum Beispiel.

List data = ...;
List newData = new ArrayList(data.size()); 

for (Iterator i = data.iterator(); i.hasNext(); ) {
    Object element = i.next();

    if ((...)) {
        newData.add(element);
    }
}

Wenn N groß ist, kann ich davon ausgehen, dass dieser Ansatz schneller ist als der remove -Ansatz für Werte von M, die nur 3 oder 4 betragen.

Es ist jedoch wichtig, newList so groß zu erstellen, dass alle Elemente in list enthalten sind, damit das Backing-Array beim Erweitern nicht kopiert wird.

1
Stephen C

Wenn Sie sich nicht sicher sind, dass das Problem tatsächlich ein Engpass ist, würde ich mich für das Lesbare entscheiden

public ArrayList filterThings() {

    ArrayList pileOfThings;
    ArrayList filteredPileOfThings = new ArrayList();

    for (Thing thingy : pileOfThings) {
        if (thingy.property != 1) {
            filteredPileOfThings.add(thingy);
        }            
    }
    return filteredPileOfThings;
}
1
thomax

Ich habe eine schnellere Alternative gefunden:

  int j = 0;
  for (Iterator i = list.listIterator(); i.hasNext(); ) {
    j++;

    if (campo.getNome().equals(key)) {
       i.remove();
       i = list.listIterator(j);
    }
  }
0
Matteo Fattore

Vielleicht Iterator ’s remove () Methode? Die JDK-Standardsammlungsklassen sollten alle Ersteller-Iteratoren enthalten, die diese Methode unterstützen.

0
Bombe

Obwohl dies nicht intuitiv ist, habe ich diesen Vorgang auf diese Weise enorm beschleunigt.

Genau das, was ich gemacht habe:

ArrayList < HashMap < String , String >> results; // This has been filled with a whole bunch of results

ArrayList <HashMap <String, String>> discard = findResultsToDiscard (Ergebnisse);

results.removeall (verwerfen);

Die Methode zum Entfernen aller Elemente benötigte jedoch mehr als 6 Sekunden (ohne die Methode zum Abrufen der Löschergebnisse), um ungefähr 800 Ergebnisse aus einem Array von 2000 (ish) zu entfernen.

Ich habe die von gustafc und anderen in diesem Beitrag vorgeschlagene Iterationsmethode ausprobiert.

Dies beschleunigte den Vorgang geringfügig (auf ungefähr 4 Sekunden), was jedoch immer noch nicht gut genug war. Also habe ich etwas riskantes ausprobiert ...

 ArrayList < HashMap < String, String>> results;

  List < Integer > noIndex = getTheDiscardedIndexs(results);

for (int j = noIndex.size()-1; j >= 0; j-- ){
    results.remove(noIndex.get(j).intValue());
}

während die getTheDiscardedIndexs ein Array von Indizes und nicht ein Array von HashMaps speichern. Dies führt zu einer schnelleren Entfernung von Objekten (jetzt ca. 0,1 Sekunden) und zu einer höheren Speichereffizienz, da keine große Anzahl von Ergebnissen zum Entfernen erstellt werden muss.

Hoffe das hilft jemandem.

0
Aiden Fry

Mit einem Iterator können Sie immer das Element behandeln, das zur Bestellung kommt, nicht einen bestimmten Index. Sie sollten sich also nicht mit der obigen Angelegenheit befassen.

Iterator itr = list.iterator();
String strElement = "";
while(itr.hasNext()){

  strElement = (String)itr.next();
  if(strElement.equals("2"))
  {
    itr.remove();
  }
0
Rakshi