Bir ArrayList veya String Array öğesinden tüm null öğeleri nasıl kaldırırım?


188

Böyle bir döngü ile deniyorum

// ArrayList tourists

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

Ama hoş değil. Biri bana daha iyi bir çözüm önerebilir mi?


Daha iyi karar vermek için bazı yararlı kriterler:

While döngüsü, Döngü ve Yineleyici Performans Testi için


2
kullanın Iterator? Java-doc kaz. download.oracle.com/javase/6/docs/api/java/util/…
Nishant

Yanıtlar:


365

Deneyin:

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

Java API'sini okuyun . Kod, java.lang.UnsupportedOperationExceptiondeğiştirilemeyen listelere (ile oluşturulmuş gibi Arrays.asList) atılacaktır ; bkz bu cevabı daha fazla ayrıntı için.


9
Zaman karmaşıklığı List.removeAll()olan n ^ 2 . Sadece söylüyorum.
Hemanth

8
Java 8 veya üstü için aşağıdaki @ MarcG'nin cevabına bakınız.
Andy Thomas

2
@Hemanth Bu zaman karmaşıklığını nasıl ele alacağınızı açıklayabilir misiniz? Oldukça görünüyor Çünkü O(n)her ikisi için bana ArrayListve LinkedList.
Helder Pereira

1
@HelderPereira Kaynak (satır 349) her iki liste arasında döngü ( tüm dizi döngüler ) gibi görünüyor ve çünkü bu tek bir öğe olduğundan , bu durumda olması gerektiğini düşünmüyorum . Ancak genellikle olurdu . contains()singletonN * 1 = NN^2
Moira

6
@Hemanth Hayır değil. Bu n * m'dir, burada m, bu durumda 1 olan tek bir null öğesinin eleman sayısıdır. O (n). Burada kaynak kodunu görebilir ve listenin bir kez okunup yazıldığını görebilir, öğeleri kaldırılanı hesaba katacak şekilde taşıyabilirsiniz.
17'de

117

2015 itibariyle, bu en iyi yoldur (Java 8):

tourists.removeIf(Objects::isNull);

Not: Bu kod, java.lang.UnsupportedOperationExceptiondeğiştirilemeyen listeler dahil olmak üzere sabit boyutlu listeler (Arrays.asList ile oluşturulmuş gibi) için atılacaktır .


1
"En iyi" ne şekilde? Diğer yaklaşımlardan daha mı hızlı? Yoksa kısalık sayesinde daha okunabilir mi?
Andy Thomas

15
Sadece kısalık yüzünden değil, daha etkileyici olduğu için. Neredeyse okuyabilirsiniz: "Turistlerden, nesne boşsa kaldırın". Ayrıca, eski yol, tek bir null nesneyle yeni bir koleksiyon oluşturmak ve ardından bir koleksiyonun içeriğini diğerinden kaldırmak istemektir. Biraz hack gibi görünüyor, değil mi? Hızla ilgili olarak, bir liste var, eğer liste gerçekten büyükse ve performans bir endişe ise, her iki yolu da test etmenizi öneririm. Tahminimce bu removeIfdaha hızlı, ama bir tahmin.
MarcG

1
Arrays.asListdeğişmez değildir . Sabit boyutlu.
turbanoff

@turbanoff evet, haklısın elbette. Sadece sabit boyutlu, cevabı güncelleyeceğim.
MarcG

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

Öyle olacak Atar UnsupportedException size vermek çünkü Arrays.asList üzerinde kullanırsanız Immutable o değiştirilemez böylece kopya. Kodun altına bakın. Değişken kopya oluşturur ve herhangi bir istisna atmaz .

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

Verimli değil, kısa

while(tourists.remove(null));

1
Ne yazık ki, çözümünüz benim için çalışan tek çözümdü ... teşekkürler!
Pkmmte

basit ve hızlı

5
Aslında hızlı olanın tam tersi. büyük bir listeniz varsa korkunç yavaş.
Gewure

18

Değişmez veri nesnelerini tercih ediyorsanız veya yalnızca giriş listesine zarar vermek istemiyorsanız, Guava'nın tahminlerini kullanabilirsiniz.

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

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

Bu, geçiş sırasında öğeleri silmeniz gerektiğinde daha yararlı olabilir. Tesadüf şu ki kullanmaya çalışmaktansa unsurları boş bırakıyordum removeAll(..null..). Teşekkürler!
Mustafa

Değerleri null olarak ayarlamak ve sonunda kaldırmak daha iyi olabilir. RemoveAll içindeki batchRemove, okuma ve yazma konumuyla listeyi tersine çevirir ve listeyi bir kez yineler, okumayı hareket ettirir, ancak bir boş değere ulaştığında yazma işlemini gerçekleştirmez. .remove () her çağrıldığında dizinin tamamını dizelemek zorunda kalabilir.
17'de

4

Java 8 öncesi kullanmanız gerekenler:

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

Java 8 sonrası kullanım:

tourists.removeIf(Objects::isNull);

Bunun nedeni zaman karmaşıklığı. Dizilerle ilgili sorun, kaldırma işleminin tamamlanması O (n) sürebilir. Gerçekten Java'da bu, boş noktayı değiştirmek için taşınan kalan öğelerin bir dizi kopyasıdır. Burada sunulan diğer birçok çözüm bu sorunu tetikleyecektir. Birincisi teknik olarak O (n * m) 'dir, burada m 1'dir, çünkü tek bir boştur: yani O (n)

Tüm singleton'ları kaldırmalısınız, dahili olarak bir okuma konumu ve bir yazma konumu olan bir batchRemove () yapar. Ve listeyi yineler. Bir null değerine ulaştığında, okuma konumunu 1 ile yineler. Aynı olduklarında geçer, farklı olduklarında değerleri kopyalamaya devam eder. Sonra sonunda boyutuna göre düzeltir.

Bunu dahili olarak etkili bir şekilde yapar:

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

Açıkça görebileceğiniz bir O (n) işlemidir.

Daha hızlı olabilen tek şey, listeyi her iki uçtan yinelemeniz ve bir null bulduğunuzda, değerini sonunda bulduğunuz değere eşit olarak ayarlamanız ve bu değeri azaltmanızdır. Ve iki değer eşleşene kadar yinelendi. Siparişi bozarsınız, ancak ayarladığınız değerlerin ve yalnız bıraktığınız değerlerin sayısını büyük ölçüde azaltırsınız. Bu bilmek için iyi bir yöntemdir ama .set () temelde ücretsiz olduğu için burada çok yardımcı olmaz, ancak bu silme şekli kemeriniz için yararlı bir araçtır.


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

Bu yeterince makul görünse de, yineleyicideki .remove () dahili olarak çağırır:

ArrayList.this.remove(lastRet);

Bu yine kaldırmanın içindeki O (n) işlemidir. Hızı önemsiyorsanız, yine istediğinizi olmayan bir System.arraycopy () yapar. Bu n ^ 2 yapar.

Ayrıca:

while(tourists.remove(null));

Hangisi O (m * n ^ 2). Burada sadece listeyi tekrarlamakla kalmıyoruz. Boş ile her eşleştiğimizde listenin tamamını tekrarlıyoruz. Sonra n / 2 (ortalama) işlemleri yapmak için System.arraycopy () kaldırmak gerçekleştirin. Kelimenin tam anlamıyla, tüm koleksiyonu değer içeren öğeler ve boş değerli öğeler arasında sıralayabilir ve bitişi daha kısa sürede kesebilirsiniz. Aslında, bu tüm kırık olanlar için geçerlidir. En azından teoride, gerçek system.arraycopy aslında pratikte bir N operasyonu değildir. Teoride, teori ve pratik aynı şeydir; pratikte değiller.


3

Tüm nulldeğerleri kaldırmanın kolay bir yolu collectionvar. Null içeren bir koleksiyonu removeAll()metoda parametre olarak geçirmeniz gerekiyor

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

yourCollection.removeAll(s1);

Bu benim için en iyisi oldu. Ayrıca, "filtre dizinize", orijinal koleksiyonun removeAll yöntemine aktarılan birden fazla giriş eklemenizi sağlar.

3

ObjectsSınıf vardır nonNull Predicateile kullanılabileceğini filter.

Örneğin:

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

1
Stack Overflow'a hoş geldiniz. Soruları cevaplarken, lütfen kodunuzun bir açıklamasını eklemeyi deneyin. Daha fazla bilgi eklemek için lütfen geri dönün ve yanıtınızı düzenleyin.
Tyler

3

Java 8'i kullanarak stream()ve tuşlarını kullanarak bunu yapabilirsiniz.filter()

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

veya

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

Daha fazla bilgi için: Java 8 - Akışlar


1
Bu çözüm, Değişken kopya ile çalışıyor yani -> List <String> listOfString = Arrays.asList ("test1", null, "test"); ..... çok! Teşekkürler
Anurag_BEHS

2

Arraylist'ten varsayılan null değerleri kaldırmanın kolay yolu

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

aksi takdirde dizgiden "null" arraylist kaldır

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

1

Bununla oynadım ve trimToSize () işlevinin işe yaradığını gördüm. Android platformunda çalışıyorum, bu yüzden farklı olabilir.


2
Javadoc'a göre trimToSize, a içeriğini değiştirmez ArrayList. Android'de bu farklıysa, muhtemelen bir hata.
fabian

1

Tüm null değerleri kaldırmak için yineleyiciyi kullanabiliriz.

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

1

Yeni bir liste oluşturmak için akış arabirimini toplama işlemi ve yardımcı bir yöntemle birlikte kullandım.

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

Temelde bu kullanıyorum:

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

Ama Java 8'i öğrendikten sonra, buna geçtim:

List.removeIf(Objects::isNull);

0

Kullanımı Java 8 bu akışları, paralel akışları ve kullanan çeşitli şekillerde yapılabilir removeIfyöntem:

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]

Paralel akış, mevcut işlemcileri kullanacak ve makul boyutlu listeler için işlemi hızlandıracaktır. Akışları kullanmadan önce her zaman karşılaştırmalı değerlendirme yapılması önerilir.


0

@Lithium answer öğesine benzer ancak "Liste null türü içeremez" hatası atmaz:

   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);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.