Jak usunąć wszystkie puste elementy z ArrayList lub String Array?


188

Próbuję z taką pętlą

// ArrayList tourists

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

Ale to nie jest miłe. Czy ktoś może zaproponować mi lepsze rozwiązanie?


Kilka przydatnych wskaźników, które pomogą podjąć lepszą decyzję:

Podczas pętli, dla testu wydajności pętli i iteratora


Odpowiedzi:


365

Próbować:

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

Przeczytaj API Java . Kod będzie rzucał java.lang.UnsupportedOperationExceptiondla niezmiennych list (takich jak utworzone przy pomocy Arrays.asList); zobacz tę odpowiedź, aby uzyskać więcej informacji.


9
Złożoność czasowa List.removeAll()wynosi n ^ 2 . Tylko mówię.
Hemanth

8
W przypadku wersji Java 8 lub nowszej zobacz odpowiedź @ MarcG poniżej.
Andy Thomas

2
@Hemanth Czy możesz rozwinąć kwestię złożoności czasu? Bo wygląda całkiem O(n)do mnie zarówno ArrayListi LinkedList.
Helder Pereira,

1
@HelderPereira Nie sądzę, że tak powinno być w tym przypadku , ponieważ źródło (linia 349) wydaje się zapętlać obie listy ( contains()zapętla całą tablicę), a ponieważ singletonjest to tylko jeden element N * 1 = N. Jakkolwiek ogólnie by tak było N^2.
Moira

6
@Hemanth Nie, to nie jest. Jest n * m, gdzie m jest liczbą elementów w tym przypadku singletonem zerowym, który wynosi 1. To O (n). Możesz zobaczyć kod źródłowy tutaj i zobaczyć, że jednorazowo czyta i zapisuje listę, przenosząc elementy do rozliczonego z usuniętego.
Tatarize

117

W 2015 r. Jest to najlepszy sposób (Java 8):

tourists.removeIf(Objects::isNull);

Uwaga: ten kod będzie java.lang.UnsupportedOperationExceptionwyświetlał listy o stałym rozmiarze (takie jak utworzone za pomocą Arrays.asList), w tym niezmienne listy.


1
„Najlepszy” w jaki sposób? Czy to jest szybsze niż inne podejścia? Czy jest to po prostu bardziej czytelne dzięki zwięzłości?
Andy Thomas

15
Nie tylko z powodu zwięzłości, ale dlatego, że jest bardziej wyrazisty. Prawie możesz to przeczytać: „Od turystów usuń, jeśli obiekt jest pusty”. Ponadto stary sposób polega na utworzeniu nowej kolekcji z pojedynczym pustym obiektem, a następnie z prośbą o usunięcie zawartości kolekcji z drugiej. To trochę hack, nie sądzisz? Jeśli chodzi o szybkość, masz rację, jeśli lista jest naprawdę duża, a wydajność stanowi problem, sugerowałbym przetestowanie na dwa sposoby. Domyślam się, że removeIfjest to szybsze, ale to przypuszczenie.
MarcG,

1
Arrays.asListnie jest niezmienny . Ma stały rozmiar.
turbanoff,

@turbanoff tak, oczywiście masz rację. Ma tylko stały rozmiar, zaktualizuję odpowiedź.
MarcG,

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

Zgłasza wyjątek UnsupportedException, jeśli użyjesz go na Arrays.asList, ponieważ daje ci niezmienną kopię, więc nie można go modyfikować. Zobacz poniżej kodu. Tworzy zmienną kopię i nie zgłasza żadnych wyjątków.

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()]);
}

18

Nie wydajny, ale krótki

while(tourists.remove(null));

1
Niestety, twoje rozwiązanie było jedynym, które działało dla mnie ... dzięki!
Pkmmte

prosty i szybki

5
@mimrahe, wręcz przeciwnie, szybko. strasznie wolne, jeśli masz dużą listę.
Gewure,

18

Jeśli wolisz niezmienne obiekty danych lub po prostu nie chcesz być destrukcyjny dla listy danych wejściowych, możesz użyć predykatów Guavy.

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))

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

Może to być bardziej przydatne, gdy trzeba usuwać elementy podczas przechodzenia. Zbieg okoliczności jest taki, że zerowałem elementy, niż próbowałem użyć removeAll(..null..). Dzięki!
Mustafa

Lepiej ustawić wartości na zero, a następnie usunąć na końcu. BatchRemove w removeAll przenosi listę z lokalizacją odczytu i zapisu i iteruje listę raz, przenosząc odczyt, ale nie zapis, gdy osiągnie wartość zerową. .remove () może zaistnieć konieczność zarchiwizowania całej tablicy za każdym razem, gdy zostanie wywołana.
Tatarize

4

Przed wersją Java 8 powinieneś używać:

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

Użycie po wersji Java 8:

tourists.removeIf(Objects::isNull);

Powodem jest złożoność czasu. Problem z tablicami polega na tym, że operacja usunięcia może zająć O (n) czasu. Naprawdę w Javie jest to tablica kopii pozostałych elementów przenoszonych w celu zastąpienia pustego miejsca. Wiele innych rozwiązań tutaj oferowanych spowoduje ten problem. Ten pierwszy jest technicznie O (n * m), gdzie m wynosi 1, ponieważ jest singletonem zerowym: więc O (n)

Powinieneś usunąćAll z singletonu, wewnętrznie robi on batchRemove (), który ma pozycję odczytu i pozycję zapisu. I iteruje listę. Kiedy osiągnie wartość zerową, po prostu iteruje pozycję odczytu o 1. Gdy są one takie same, mija, kiedy są różne, przesuwa się dalej, kopiując wartości. Następnie na końcu przycina się do rozmiaru.

Skutecznie robi to wewnętrznie:

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();
    }
}

To, co wyraźnie widać, to operacja O (n).

Jedyną rzeczą, która mogłaby być szybsza, jest iteracja listy z obu końców, a po znalezieniu wartości null ustawia się jej wartość równą wartości znalezionej na końcu i zmniejsza tę wartość. I iterował, aż obie wartości się zgadzały. Zepsujesz porządek, ale znacznie zmniejszysz liczbę ustawionych wartości w porównaniu z wartościami, które pozostawiłeś sam. Co jest dobrą metodą na poznanie, ale tutaj niewiele pomoże, ponieważ .set () jest w zasadzie darmowy, ale ta forma usuwania jest przydatnym narzędziem dla twojego paska.


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

Chociaż wydaje się to rozsądne, funkcja .remove () iteratora wywołuje wewnętrznie:

ArrayList.this.remove(lastRet);

To znowu operacja O (n) w usuwaniu. Robi System.arraycopy (), który znowu nie jest tym, czego chcesz, jeśli zależy ci na szybkości. To sprawia, że ​​n ^ 2.

Jest także:

while(tourists.remove(null));

Który jest O (m * n ^ 2). Tutaj nie tylko iterujemy listę. Powtarzamy całą listę za każdym razem, gdy dopasowujemy zero. Następnie wykonujemy n / 2 (przeciętne) operacje, aby wykonać System.arraycopy () w celu wykonania operacji usuwania. Można dosłownie posortować całą kolekcję między elementami o wartościach i elementami o wartościach zerowych i przyciąć zakończenie w krótszym czasie. W rzeczywistości dotyczy to wszystkich zepsutych. Przynajmniej teoretycznie faktyczny system. Arraycopy nie jest w rzeczywistości operacją N. Teoretycznie teoria i praktyka są tym samym; w praktyce nie są.


3

Istnieje prosty sposób usunięcia wszystkich nullwartości z. collectionMusisz przekazać kolekcję zawierającą null jako parametr do removeAll()metody

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

yourCollection.removeAll(s1);

To działało dla mnie najlepiej. Pozwala także łatwo dodać więcej niż jeden wpis do „tablicy filtrów”, który jest przekazywany do metody removeAll oryginalnej kolekcji.

3

ObjectsKlasa ma nonNull Predicate, że może być stosowany z filter.

Na przykład:

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

1
Witamy w Stack Overflow. Odpowiadając na pytania, spróbuj dodać wyjaśnienie swojego kodu. Wróć i edytuj swoją odpowiedź, aby podać więcej informacji.
Tyler

3

Korzystając z Java 8, możesz to zrobić za pomocą stream()ifilter()

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

lub

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

Więcej informacji: Java 8 - Strumienie


1
To rozwiązanie działa z niezmienną kopią, tj. -> List <String> listOfString = Arrays.asList („test1”, null, „test”); ..... też ! Dzięki
Anurag_BEHS

2

Jest to prosty sposób na usunięcie domyślnych wartości zerowych z listy arraylist

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

w przeciwnym razie wartość ciągu „null” usuń z tablicy

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

1

Bawiłem się tym i dowiedziałem się, że trimToSize () wydaje się działać. Pracuję na platformie Android, więc może być inaczej.


2
Według javadoc, trimToSizenie modyfikuje zawartości ArrayList. Jeśli jest inaczej w Androidzie, prawdopodobnie jest to błąd.
fabian

1

Możemy użyć iteratora do tego samego, aby usunąć wszystkie wartości null.

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

1

Użyłem interfejsu strumienia wraz z kolekcjonowaniem operacji strumienia i metodą pomocniczą do wygenerowania nowej listy.

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

private <T> boolean isNotNull(final T item) {
    return  item != null;
}

2
tourists.stream().filter(s -> s != null).collect(Collectors.toList());
1ac0

1

Głównie używam tego:

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

Ale kiedy nauczyłem się Java 8, przerzuciłem się na to:

List.removeIf(Objects::isNull);

0

Za pomocą Java 8 można to zrobić na różne sposoby, używając strumieni, równoległych strumieni i removeIfmetody:

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]

Strumień równoległy wykorzysta dostępne procesory i przyspieszy proces dla list o rozsądnych rozmiarach. Zawsze zaleca się przeprowadzenie testu porównawczego przed użyciem strumieni.


0

Podobne do odpowiedzi @Lithium, ale nie generuje błędu „Lista nie może zawierać typu null”:

   list.removeAll(Collections.<T>singleton(null));

0
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.