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.