Null değerler için jdk8 akışını nasıl yönetmeliyiz


88

Merhaba Java geliştiricileri,

in advanceJDK8 henüz piyasaya sürülmediğinden (ve şimdilik değil ..) konunun biraz olabileceğini biliyorum, ancak Lambda ifadeleri ve özellikle de Stream olarak bilinen yeni koleksiyon API'siyle ilgili kısım hakkında bazı makaleler okuyordum.

İşte Java Magazine makalesinde verilen örnek (su samuru popülasyon algoritmasıdır ..):

Set<Otter> otters = getOtters();
System.out.println(otters.stream()
    .filter(o -> !o.isWild())
    .map(o -> o.getKeeper())
    .filter(k -> k.isFemale())
    .into(new ArrayList<>())
    .size());

Sorum şu: Set iç yinelemesinin ortasında su samurlarından biri boşsa ne olur?

Bir NullPointerException atılmasını beklerdim ama belki de hala önceki geliştirme paradigmasında (işlevsel olmayan) takılıp kaldım, bunun nasıl ele alınması gerektiği konusunda biri beni aydınlatabilir mi?

Bu gerçekten bir NullPointerException oluşturursa, özelliği oldukça tehlikeli buluyorum ve yalnızca aşağıdaki gibi kullanılması gerekecek:

  • Boş değer olmadığından emin olmak için geliştirici (belki önceki bir .filter kullanarak (o -> o! = Null))
  • Uygulamanın hiçbir zaman boş su samuru veya ilgilenilecek özel bir NullOtter nesnesi oluşturmadığından emin olmak için geliştirici.

En iyi seçenek veya başka bir seçenek nedir?

Teşekkürler!


3
Burada doğru şeyi yapmanın programcıya bağlı olduğunu söyleyebilirim; JVM ve derleyici yalnızca bu kadarını yapabilir. Ancak bazı koleksiyon uygulamalarının boş değerlere izin vermeyeceğini unutmayın.
fge

19
Sen kullanabilirsiniz filter(Objects::nonNull)ile Objectsgelenjava.utils
Benj

Yanıtlar:


44

Mevcut düşünce, bazı operasyonlar daha az toleranslı olmasına ve NPE atmasına neden olabilmesine rağmen, genel olarak bunlara izin vermek için boşları "tolere etmek" gibi görünmektedir. Lambda Kitaplıkları uzman grubu posta listesindeki, özellikle bu iletideki boş değerlere ilişkin tartışmaya bakın . Daha sonra 3. seçenek hakkında fikir birliği ortaya çıktı (Doug Lea'dan önemli bir itirazla). Yani evet, OP'nin boru hatlarının NPE ile patlamasıyla ilgili endişesi geçerli.

Tony Hoare'nin boşları "Milyar Dolarlık Hata" olarak adlandırması boşuna değil . Boşluklarla uğraşmak gerçek bir acıdır. Klasik koleksiyonlarda bile (lambdalar veya akışlar dikkate alınmadan) boş değerler sorunludur. Gibi FGE Bir yorumda, bazı koleksiyonları nulls ve diğerleri yok tanır. Boş değerlere izin veren koleksiyonlarla, bu API'ye belirsizlikler getirir. Örneğin, Map.get () ile boş bir dönüş, anahtarın mevcut olduğunu ve değerinin boş olduğunu veya anahtarın olmadığını gösterir. Bu vakaları netleştirmek için fazladan iş yapmak gerekiyor.

Null için olağan kullanım, bir değerin yokluğunu belirtmektir. Java SE 8 için önerilen bununla başa çıkma yaklaşımı java.util.Optional, bir değerin varlığını / yokluğunu, bir varsayılan değer sağlama, bir istisna atma veya bir işlevi çağırma vb. Davranışlarla birlikte özetleyen yeni bir tür sunmaktır. değer yok. Optionalyalnızca yeni API'ler tarafından kullanılır, ancak sistemdeki diğer her şey yine de boş değer olasılığına katlanmak zorundadır.

Benim tavsiyem, mümkün olduğunca gerçek boş referanslardan kaçınmaktır. Nasıl "boş" bir Su Samuru olabileceği verilen örnekten anlamak zor. Ancak gerekliyse, OP'nin null değerleri filtreleme veya onları bir sentinel nesneye ( Null Object Pattern ) eşleme önerileri iyi yaklaşımlardır.


3
Neden boş değerlerden kaçının? Veritabanlarında yoğun olarak kullanılırlar.
Raffi Khatchadourian

6
@RaffiKhatchadourian Evet, veritabanlarında boş değerler kullanılır, ancak aynı derecede sorunludur. Bunu ve buna bakın ve tüm cevapları ve yorumları okuyun . Ayrıca SQL boş değerinin boole ifadeleri üzerindeki etkisini de göz önünde bulundurun: en.wikipedia.org/wiki/… ... bu zengin sorgu hataları kaynağıdır.
Stuart Marks

1
@RaffiKhatchadourian Çünkü nullberbat, nedeni bu. Acc. bir ankete göre (bulamıyorum), NPE Java'da 1 numaralı istisnadır. SQL yüksek seviyeli bir programlama dili değildir, kesinlikle işlevsel değildir ve boşluğu küçümser, bu yüzden umursamaz.
Abhijit Sarkar

95

Cevaplar% 100 doğru olsa da null, isteğe bağlı ile listenin kendi vaka işlemlerini iyileştirmek için küçük bir öneri :

 List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

Parça , null Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList)olduğunda listOfStuffdurumu güzelce işlemenize ve NullPointerException ile başarısız olmak yerine bir emptyList döndürmenize olanak tanır .


2
Bunu beğendim, açıkça boş değeri kontrol etmekten kaçınıyor.
Chris

1
bu çok net görünüyor .. güzel ve tam da ihtiyacım olan şey
Abdullah Al Noman

Açıkça boş değeri kontrol ediyor, sadece farklı bir şekilde. Boş olmasına izin verilirse, İsteğe bağlı olmalıdır, fikir bu, değil mi? Tüm boş değerleri isteğe bağlı olarak yasaklayın. Dahası, boş bir liste yerine null döndürmek kötü bir uygulamadır, bu yüzden şunu görürsem iki kod kokusu gösterir: Kullanılmakta olan hiçbir Seçenek ve boş akış döndürülmez. Ve ikincisi 20 yıldan fazla bir süredir mevcut, bu yüzden bu olgun ...
Koos Gadellaa

boş bir liste yerine boş bir liste döndürmek istersem ne olur?
Ashburn RK

@AshburnRK bu kötü bir uygulama. Boş bir liste iade etmelisiniz.
Johnny

70

Stuart'ın cevabı harika bir açıklama sağlıyor, ancak başka bir örnek vermek istiyorum.

reduceBoş değerler içeren bir Akış üzerinde bir gerçekleştirmeye çalışırken bu sorunla karşılaştım (aslında öyleydi LongStream.average(), bir tür indirgeme). Ortalama () döndüğünden OptionalDouble, Akışın nulllar içerebileceğini varsaydım, bunun yerine bir NullPointerException atıldı. Bu, Stuart'ın null v. Boş açıklaması nedeniyledir .

Öyleyse, OP'nin önerdiği gibi, şöyle bir filtre ekledim:

list.stream()
    .filter(o -> o != null)
    .reduce(..);

Veya aşağıda belirtildiği gibi, Java API tarafından sağlanan yüklemi kullanın:

list.stream()
    .filter(Objects::nonNull)
    .reduce(..);

Stuart bağlantılı posta listesi tartışmasından: Brian Goetz, Akışlardaki nulllar üzerine


19

Bir akıştan yalnızca boş değerleri filtrelemek istiyorsanız, java.util.Objects.nonNull (Object) için basitçe bir yöntem başvurusu kullanabilirsiniz . Belgelerinden:

Bu yöntem bir Dayanak olarak kullanılmak üzere mevcuttur ,filter(Objects::nonNull)

Örneğin:

List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);

list.stream()
    .filter( Objects::nonNull )  // <-- Filter out null values
    .forEach( System.out::println );

Bu yazdıracak:

Foo
Bar

7

Boş değerden nasıl kaçınılacağına bir örnek, örneğin gruplamadan önce filtre kullanın

Gruplamadan önce boş örnekleri filtreleyinBy.

İşte bir örnek

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
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.