Wie entferne ich alle Nullelemente aus einer ArrayList oder einem String-Array?

187

Ich versuche es mit einer solchen Schleife

// ArrayList tourists

for (Tourist t : tourists) {
    if (t != null) {     
        t.setId(idForm); 
    }   
}

Aber es ist nicht schön. Kann mir jemand eine bessere Lösung vorschlagen?


Einige nützliche Benchmarks, um eine bessere Entscheidung zu treffen:

While-Schleife, For-Schleife und Iterator-Leistungstest

Juan de Dios
quelle
2
verwenden Iterator? Dig Java-Doc. download.oracle.com/javase/6/docs/api/java/util/…
Nishant

Antworten:

365

Versuchen:

tourists.removeAll(Collections.singleton(null));

Lesen Sie die Java-API . Der Code löst java.lang.UnsupportedOperationExceptionunveränderliche Listen aus (z. B. erstellt mit Arrays.asList). Weitere Informationen finden Sie in dieser Antwort .

Lithium
quelle
9
Die zeitliche Komplexität von List.removeAll()ist n ^ 2 . Nur sagen.
Hemanth
8
Informationen zu Java 8 oder höher finden Sie in der Antwort von @ MarcG unten.
Andy Thomas
2
@Hemanth Kannst du näher erläutern, wie du zu dieser zeitlichen Komplexität gekommen bist? Denn es sieht ganz O(n)mir für beide ArrayListund LinkedList.
Helder Pereira
1
@HelderPereira Ich denke nicht, dass dies für diesen Fall der Fall sein sollte , da die Quelle (Zeile 349) beide Listen zu durchlaufen scheint ( contains()das gesamte Array durchläuft ) und da dies singletonnur ein Element ist N * 1 = N. Im Allgemeinen wäre es jedoch so N^2.
Moira
6
@Hemanth Nein, ist es nicht. Es ist n * m, wobei m die Anzahl der Elemente ist, in diesem Fall ein Singleton von null, der 1 ist. Es ist O (n). Sie können den Quellcode hier sehen und sehen, dass er die Liste einmal liest und überschreibt, wobei die Elemente verschoben werden, um den entfernten Code zu berücksichtigen.
Tatarize
116

Ab 2015 ist dies der beste Weg (Java 8):

tourists.removeIf(Objects::isNull);

Hinweis: Dieser Code wird java.lang.UnsupportedOperationExceptionfür Listen mit fester Größe (z. B. mit Arrays.asList erstellt) einschließlich unveränderlicher Listen ausgegeben.

MarcG
quelle
1
"Best" auf welche Weise? Ist es schneller als andere Ansätze? Oder ist es nur der Kürze halber lesbarer?
Andy Thomas
15
Nicht nur wegen der Kürze, sondern auch, weil es ausdrucksvoller ist. Sie können es fast lesen: "Von Touristen entfernen, wenn Objekt null ist". Der alte Weg besteht auch darin, eine neue Sammlung mit einem einzelnen Nullobjekt zu erstellen und dann zu fragen, ob der Inhalt einer Sammlung aus dem anderen entfernt werden soll. Scheint ein bisschen hackig zu sein, findest du nicht? In Bezug auf die Geschwindigkeit haben Sie einen Punkt: Wenn die Liste wirklich groß ist und die Leistung ein Problem darstellt, würde ich vorschlagen, beide Methoden zu testen. Meine Vermutung wäre, dass removeIfdas schneller ist, aber es ist eine Vermutung.
MarcG
1
Arrays.asListist nicht unveränderlich . Es hat eine feste Größe.
Turbanoff
@turbanoff ja, du hast natürlich recht. Es ist nur eine feste Größe, ich werde die Antwort aktualisieren.
MarcG
46
list.removeAll(Collections.singleton(null));

Es wird eine nicht unterstützte Ausnahme ausgelöst, wenn Sie sie in Arrays.asList verwenden, da Sie eine unveränderliche Kopie erhalten, sodass sie nicht geändert werden kann. Siehe unten den Code. Es schafft Mutable Kopie und wird keine Ausnahme ausgelöst.

public static String[] clean(final String[] v) {
    List<String> list = new ArrayList<String>(Arrays.asList(v));
    list.removeAll(Collections.singleton(null));
    return list.toArray(new String[list.size()]);
}
AZ_
quelle
18

Nicht effizient, aber kurz

while(tourists.remove(null));
Peter Lawrey
quelle
1
Leider war Ihre Lösung die einzige, die für mich funktioniert hat ... danke!
Pkmmte
einfach und schnell
5
@mimrahe das Gegenteil von schnell, eigentlich. schrecklich langsam, wenn Sie eine große Liste haben.
Gewure
18

Wenn Sie unveränderliche Datenobjekte bevorzugen oder die Eingabeliste nicht destruktiv beeinflussen möchten, können Sie die Prädikate von Guava verwenden.

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))
James Kojo
quelle
7
 for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }
Mat Mannion
quelle
Dies kann nützlicher sein, wenn Sie beim Durchlaufen Elemente löschen müssen. Zufall ist, dass ich die Elemente auf Null gesetzt habe, als ich versucht habe, sie zu verwenden removeAll(..null..). Vielen Dank!
Mustafa
Möglicherweise ist es besser, die Werte auf Null zu setzen, als sie am Ende zu entfernen. Der BatchRemove in removeAll durchläuft die Liste mit einem Lese- und Schreibspeicherort und iteriert die Liste einmal, wobei der Lesevorgang, nicht jedoch der Schreibvorgang verschoben wird, wenn eine Null erreicht wird. .remove () Möglicherweise muss das gesamte Array bei jedem Aufruf automatisch kopiert werden.
Tatarize
4

Vor Java 8 sollten Sie Folgendes verwenden:

tourists.removeAll(Collections.singleton(null));

Verwendung nach Java 8:

tourists.removeIf(Objects::isNull);

Der Grund hierfür ist die zeitliche Komplexität. Das Problem bei Arrays besteht darin, dass ein Entfernungsvorgang O (n) Zeit in Anspruch nehmen kann. In Java ist dies eine Array-Kopie der verbleibenden Elemente, die verschoben werden, um die leere Stelle zu ersetzen. Viele andere hier angebotene Lösungen lösen dieses Problem aus. Ersteres ist technisch gesehen O (n * m), wobei m 1 ist, weil es eine Singleton-Null ist: also O (n)

Sie solltenAlle Singleton entfernen, intern führt es einen batchRemove () aus, der eine Lese- und eine Schreibposition hat. Und iteriert die Liste. Wenn es eine Null trifft, iteriert es einfach die Leseposition um 1. Wenn sie gleich sind, passiert es, wenn sie unterschiedlich sind, fährt es fort, die Werte zu kopieren. Am Ende wird es dann zugeschnitten.

Dies geschieht effektiv intern:

public static <E> void removeNulls(ArrayList<E> list) {
    int size = list.size();
    int read = 0;
    int write = 0;
    for (; read < size; read++) {
        E element = list.get(read);
        if (element == null) continue;
        if (read != write) list.set(write, element);
        write++;
    }
    if (write != size) {
        list.subList(write, size).clear();
    }
}

Was Sie explizit sehen können, ist eine O (n) -Operation.

Das einzige, was jemals schneller sein könnte, ist, wenn Sie die Liste von beiden Seiten iterieren und wenn Sie eine Null gefunden haben, setzen Sie ihren Wert gleich dem Wert, den Sie am Ende gefunden haben, und dekrementieren Sie diesen Wert. Und iteriert, bis die beiden Werte übereinstimmen. Sie würden die Reihenfolge durcheinander bringen, aber die Anzahl der von Ihnen festgelegten Werte im Vergleich zu denen, die Sie alleine gelassen haben, erheblich reduzieren. Dies ist eine gute Methode, die Sie kennen sollten, die hier jedoch nicht viel hilft, da .set () grundsätzlich kostenlos ist. Diese Form des Löschens ist jedoch ein nützliches Werkzeug für Ihren Gürtel.


for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }

Während dies vernünftig erscheint, ruft die .remove () auf dem Iterator intern auf:

ArrayList.this.remove(lastRet);

Welches ist wieder die O (n) -Operation innerhalb der Entfernung. Es führt eine System.arraycopy () durch, die wiederum nicht Ihren Wünschen entspricht, wenn Sie Wert auf Geschwindigkeit legen. Dies macht es n ^ 2.

Es gibt auch:

while(tourists.remove(null));

Welches ist O (m * n ^ 2). Hier iterieren wir nicht nur die Liste. Wir wiederholen die gesamte Liste jedes Mal, wenn wir mit der Null übereinstimmen. Dann führen wir n / 2 (durchschnittliche) Operationen aus, um die System.arraycopy () durchzuführen, um das Entfernen durchzuführen. Sie können buchstäblich die gesamte Sammlung zwischen Elementen mit Werten und Elementen mit Nullwerten sortieren und das Ende in kürzerer Zeit kürzen. In der Tat gilt das für alle kaputten. Zumindest theoretisch ist die eigentliche system.arraycopy in der Praxis keine N-Operation. In Theorie, Theorie und Praxis sind dasselbe; in der Praxis sind sie nicht.

Tatarisieren
quelle
3

Es gibt eine einfache Möglichkeit, alle nullWerte aus zu collectionentfernen. Sie müssen eine Auflistung mit null als Parameter an die removeAll()Methode übergeben

List s1=new ArrayList();
s1.add(null);

yourCollection.removeAll(s1);
shiv
quelle
Das hat bei mir am besten funktioniert. Außerdem können Sie problemlos mehr als einen Eintrag in Ihr "Filterarray" einfügen, der an die removeAll-Methode der ursprünglichen Sammlung übergeben wird.
3

Die ObjectsKlasse hat eine nonNull Predicate, mit der verwendet werden kann filter.

Beispielsweise:

tourists.stream().filter(Objects::nonNull).collect(Collectors.toList());
JeffF
quelle
1
Willkommen bei Stack Overflow. Versuchen Sie bei der Beantwortung von Fragen, eine Erklärung Ihres Codes hinzuzufügen. Bitte gehen Sie zurück und bearbeiten Sie Ihre Antwort, um weitere Informationen aufzunehmen.
Tyler
3

Mit Java 8 können Sie dies mit stream()und tunfilter()

tourists = tourists.stream().filter(t -> t != null).collect(Collectors.toList())

oder

tourists = tourists.stream().filter(Objects::nonNull).collect(Collectors.toList())

Für weitere Informationen: Java 8 - Streams

Jad Chahine
quelle
1
Diese Lösung funktioniert mit unveränderlicher Kopie, dh -> List <String> listOfString = Arrays.asList ("test1", null, "test"); ..... auch ! Danke
Anurag_BEHS
2

Dies ist eine einfache Möglichkeit, Standard-Nullwerte aus der Arrayliste zu entfernen

     tourists.removeAll(Arrays.asList(null));  

Andernfalls wird der Zeichenfolgenwert "null" aus der Arrayliste entfernt

       tourists.removeAll(Arrays.asList("null"));  
Jobin_vibes
quelle
1

Ich habe damit herumgespielt und herausgefunden, dass trimToSize () zu funktionieren scheint. Ich arbeite an der Android-Plattform, daher könnte es anders sein.

der Blitz
quelle
2
Ändert laut Javadoc trimToSizenicht den Inhalt von a ArrayList. Wenn dies in Android anders ist, ist es wahrscheinlich ein Fehler.
Fabian
1

Wir können den Iterator für denselben verwenden, um alle Nullwerte zu entfernen.

Iterator<Tourist> itr= tourists.iterator();
while(itr.hasNext()){
    if(itr.next() == null){
        itr.remove();
    }
}
amit
quelle
1

Ich habe die Stream-Schnittstelle zusammen mit der Stream-Operation Collect und einer Hilfsmethode verwendet, um eine neue Liste zu erstellen .

tourists.stream().filter(this::isNotNull).collect(Collectors.toList());

private <T> boolean isNotNull(final T item) {
    return  item != null;
}
Mabi
quelle
2
tourists.stream().filter(s -> s != null).collect(Collectors.toList());
1ac0
1

Hauptsächlich benutze ich das:

list.removeAll(Collections.singleton(null));

Aber nachdem ich Java 8 gelernt hatte, wechselte ich zu folgendem:

List.removeIf(Objects::isNull);
Maged
quelle
0

Mit Java 8 kann dies auf verschiedene Arten mithilfe von Streams, parallelen Streams und removeIfMethoden durchgeführt werden:

List<String> stringList = new ArrayList<>(Arrays.asList(null, "A", "B", null, "C", null));
List<String> listWithoutNulls1 = stringList.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
List<String> listWithoutNulls2 = stringList.parallelStream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
stringList.removeIf(Objects::isNull); //[A,B,C]

Der parallele Stream verwendet verfügbare Prozessoren und beschleunigt den Prozess für Listen mit angemessener Größe. Es ist immer ratsam, vor der Verwendung von Streams ein Benchmarking durchzuführen.

akhil_mittal
quelle
0

Ähnlich wie bei der Antwort von @Lithium, aber es wird kein Fehler "Liste darf keinen Typ null enthalten" ausgegeben:

   list.removeAll(Collections.<T>singleton(null));
HannahCarney
quelle
0
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);
cunhaf
quelle