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

Juan de Dios
źródło
2
użyć Iterator? Dig java-doc. download.oracle.com/javase/6/docs/api/java/util/…
Nishant

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.

Lit
źródło
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.

MarcG
źródło
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()]);
}
AZ_
źródło
18

Nie wydajny, ale krótki

while(tourists.remove(null));
Peter Lawrey
źródło
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()))
James Kojo
źródło
7
 for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }
Mat Mannion
źródło
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ą.

Tatarize
źródło
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);
Shiv
źródło
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());
JeffF
źródło
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

Jad Chahine
źródło
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"));  
Jobin_vibes
źródło
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.

theblitz
źródło
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();
    }
}
amit
źródło
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;
}
Mabi
źródło
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);
Mag
źródło
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.

akhil_mittal
źródło
0

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

   list.removeAll(Collections.<T>singleton(null));
Hannah Carney
źródło
0
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);
cunhaf
źródło