Bagaimana cara menghapus semua elemen nol dari ArrayList atau String Array?


188

Saya coba dengan loop seperti itu

// ArrayList tourists

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

Tapi itu tidak baik. Adakah yang bisa menyarankan saya solusi yang lebih baik?


Beberapa tolok ukur yang berguna untuk membuat keputusan yang lebih baik:

Sementara loop, Untuk loop dan Tes Kinerja Iterator


2

Jawaban:


365

Mencoba:

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

Baca API Java . Kode akan dilemparkan java.lang.UnsupportedOperationExceptionuntuk daftar yang tidak dapat diubah (seperti dibuat dengan Arrays.asList); lihat jawaban ini untuk lebih jelasnya.


9
Kompleksitas waktu List.removeAll()adalah n ^ 2 . Hanya mengatakan.
Hemanth

8
Untuk Java 8 atau lebih baru, lihat jawaban @ MarcG di bawah ini.
Andy Thomas

2
@Hemanth Bisakah Anda menguraikan bagaimana Anda mendapatkan kompleksitas waktu itu? Karena itu terlihat cukup O(n)bagi saya untuk keduanya ArrayListdan LinkedList.
Helder Pereira

1
@HelderPereira Saya tidak berpikir seharusnya untuk kasus ini , karena sumber (baris 349) tampaknya untuk mengulang kedua daftar ( contains()loop seluruh array) dan karena singletonhanya satu elemen yang akan terjadi N * 1 = N. Namun umumnya itu akan terjadi N^2.
Moira

6
@Hantant Tidak tidak. Ini n * m di mana m adalah jumlah elemen dalam hal ini singleton dari nol yaitu 1. Ini O (n). Anda dapat melihat kode sumber di sini dan melihatnya membaca dan menulis di atas daftar satu kali, memindahkan elemen ke akun yang sudah di remvo.
Tatarize

117

Pada 2015, ini adalah cara terbaik (Java 8):

tourists.removeIf(Objects::isNull);

Catatan: Kode ini akan digunakan java.lang.UnsupportedOperationExceptionuntuk daftar dengan ukuran tetap (seperti dibuat dengan Arrays.asList), termasuk daftar yang tidak dapat diubah.


1
"Terbaik" dalam hal apa? Apakah lebih cepat dari pendekatan lain? Atau apakah itu lebih mudah dibaca berdasarkan singkatnya?
Andy Thomas

15
Bukan hanya karena singkatnya, tetapi karena lebih ekspresif. Anda hampir dapat membacanya: "Dari turis, hapus jika objeknya nol". Selain itu, cara lama adalah membuat koleksi baru dengan objek nol tunggal, dan kemudian meminta untuk menghapus konten koleksi dari yang lain. Sepertinya sedikit peretasan, bukan begitu? Mengenai kecepatan, Anda ada benarnya, jika daftar ini benar-benar besar dan kinerja menjadi perhatian, saya sarankan menguji kedua cara. Dugaan saya removeIfadalah yang lebih cepat, tetapi ini hanya dugaan.
MarcG

1
Arrays.asListtidak kekal . Ukurannya sudah diperbaiki.
Turbanoff

@turbanoff ya, Anda benar, tentu saja. Hanya untuk ukuran tertentu, saya akan memperbarui jawabannya.
MarcG

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

Ini akan Melempar UnsupportedException jika Anda menggunakannya pada Arrays.asList karena memberikan Berubah copy sehingga tidak dapat diubah. Lihat di bawah kode. Ini menciptakan salinan yang dapat diubah dan tidak akan membuang pengecualian.

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

Tidak efisien, tetapi pendek

while(tourists.remove(null));

1
Sayangnya, solusi Anda adalah satu-satunya yang bekerja untuk saya ... terima kasih!
Pkmmte

sederhana dan cepat

5
@ Mimrahe kebalikan dari puasa, sebenarnya. lambat lambat jika Anda memiliki daftar besar.
Gewure

18

Jika Anda lebih suka objek data yang tidak dapat diubah, atau jika Anda tidak ingin merusak daftar input, Anda dapat menggunakan predikat Guava.

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

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

Ini bisa lebih berguna ketika Anda harus menghapus elemen saat melintasi. Kebetulan saya membatalkan elemen daripada mencoba menggunakan removeAll(..null..). Terima kasih!
Mustafa

Anda mungkin lebih baik mengatur nilai ke nol kemudian menghapus di akhir. BatchHapus di removeAll mentransversikan daftar, dengan lokasi baca dan tulis dan iterasi daftar sekali, memindahkan baca tetapi tidak menulis ketika menyentuh nol. .remove () mungkin ada yang sah harus arraycopy seluruh array setiap kali dipanggil.
Tatarize

4

Pre-Java 8 Anda harus menggunakan:

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

Penggunaan Post-Java 8:

tourists.removeIf(Objects::isNull);

Alasannya di sini adalah kompleksitas waktu. Masalah dengan array adalah bahwa operasi penghapusan dapat mengambil O (n) waktu untuk menyelesaikannya. Sungguh di Jawa ini adalah salinan array elemen yang tersisa dipindahkan untuk menggantikan tempat kosong. Banyak solusi lain yang ditawarkan di sini akan memicu masalah ini. Yang pertama secara teknis O (n * m) di mana m adalah 1 karena itu adalah nol tunggal: jadi O (n)

Anda harus menghapus semua singleton, secara internal ia melakukan batchRemove () yang memiliki posisi baca dan posisi tulis. Dan mengulang daftar. Ketika menyentuh nol, itu hanya mengulangi posisi baca oleh 1. Ketika mereka sama dengan yang dilewatinya, ketika mereka berbeda itu terus bergerak bersama menyalin nilai-nilai. Kemudian pada akhirnya dipotong untuk ukuran.

Ini secara efektif melakukan ini secara internal:

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

Yang dapat Anda lihat secara eksplisit adalah operasi O (n).

Satu-satunya hal yang bisa lebih cepat adalah jika Anda mengulang daftar dari kedua ujungnya, dan ketika Anda menemukan nol, Anda menetapkan nilainya sama dengan nilai yang Anda temukan di akhir, dan mengurangi nilai itu. Dan diulang sampai dua nilai cocok. Anda telah mengacaukan pesanan, tetapi akan sangat mengurangi jumlah nilai yang Anda set vs yang Anda tinggalkan sendiri. Yang merupakan metode yang bagus untuk diketahui tetapi tidak akan banyak membantu di sini karena .set () pada dasarnya gratis, tetapi bentuk delete adalah alat yang berguna untuk sabuk Anda.


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

Meskipun ini tampaknya cukup masuk akal, .remove () pada iterator secara internal memanggil:

ArrayList.this.remove(lastRet);

Yang lagi operasi O (n) dalam menghapus. Itu System.arraycopy () yang lagi-lagi bukan apa yang Anda inginkan, jika Anda peduli kecepatan. Ini membuatnya n ^ 2.

Ada juga:

while(tourists.remove(null));

Yaitu O (m * n ^ 2). Di sini kita tidak hanya mengulang daftar. Kami mengulangi seluruh daftar, setiap kali kami cocok dengan nol. Kemudian kami melakukan operasi n / 2 (rata-rata) untuk melakukan System.arraycopy () untuk melakukan penghapusan. Anda dapat benar-benar mengurutkan seluruh koleksi antara item dengan nilai dan item dengan nilai nol dan memotong akhir dalam waktu yang lebih singkat. Bahkan, itu berlaku untuk semua yang rusak. Setidaknya dalam teori, sistem yang sebenarnya. Arraycopy sebenarnya bukan operasi N dalam praktek. Secara teori, teori dan praktik adalah hal yang sama; dalam praktiknya tidak.


3

Ada cara mudah untuk menghapus semua nullnilai dari collection. Anda harus melewati koleksi yang berisi null sebagai parameter ke removeAll()metode

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

yourCollection.removeAll(s1);

Ini yang terbaik bagi saya. Hal ini juga memungkinkan Anda untuk dengan mudah menambahkan lebih dari satu entri di "array filter" Anda yang diteruskan ke metode removeAll dari koleksi asli.

3

The Objectskelas memiliki nonNull Predicateyang dapat digunakan dengan filter.

Sebagai contoh:

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

1
Selamat datang di Stack Overflow. Saat menjawab pertanyaan, coba tambahkan penjelasan tentang kode Anda. Harap kembali dan edit jawaban Anda untuk memasukkan lebih banyak informasi.
Tyler

3

Menggunakan Java 8, Anda dapat melakukan ini menggunakan stream()danfilter()

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

atau

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

Untuk info lebih lanjut: Java 8 - Streaming


1
Solusi ini bekerja dengan salinan Immutable yaitu -> Daftar <String> listOfString = Arrays.asList ("test1", null, "test"); ..... juga! Terima kasih
Anurag_BEHS

2

Ini adalah cara mudah untuk menghapus nilai null default dari daftar array

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

jika tidak, nilai string "null" hapus dari arraylist

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

1

Saya bermain-main dengan ini dan menemukan bahwa trimToSize () tampaknya berfungsi. Saya bekerja di platform Android jadi mungkin berbeda.


2
Menurut javadoc, trimToSizetidak mengubah konten a ArrayList. Jika ini berbeda di android, itu mungkin bug.
Fabian

1

Kita dapat menggunakan iterator untuk hal yang sama untuk menghapus semua nilai nol.

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

1

Saya menggunakan antarmuka aliran bersama-sama dengan operasi aliran mengumpulkan dan metode penolong untuk menghasilkan daftar baru.

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

1

Terutama saya menggunakan ini:

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

Tetapi setelah saya mempelajari Java 8, saya beralih ke ini:

List.removeIf(Objects::isNull);

0

Menggunakan Java 8 ini dapat dilakukan dengan berbagai cara menggunakan aliran, aliran paralel dan removeIfmetode:

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]

Aliran paralel akan menggunakan prosesor yang tersedia dan akan mempercepat proses untuk daftar berukuran wajar. Itu selalu disarankan untuk benchmark sebelum menggunakan stream.


0

Mirip dengan jawaban @Lithium tetapi tidak melempar kesalahan "Daftar mungkin tidak mengandung tipe 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);
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.