Değerleri yalnızca Java8'de lambda kullanarak null değilse filtreleyin


160

Diyelim ki nesnelerin bir listesi var car. Bu listeyi Java 8 kullanarak bazı parametrelere göre filtrelemek istiyorum. Ancak parametre ise null, atar NullPointerException. Boş değerler nasıl filtrelenir?

Mevcut kod aşağıdaki gibidir

requiredCars = cars.stream().filter(c -> c.getName().startsWith("M"));

Bu atar NullPointerExceptioneğer getName()getiri null.


“Yalnızca boş değilse değerleri filtrelemek” veya “boş değerleri filtrelemek” istiyor musunuz? Bu bana aykırı geliyor.
Holger

3
Tunaki'nin cevabını, sorunuzu gerçekten cevaplayan tek kişi olarak göründüğünü kabul etmenizi önerebilir miyim ?
Mark Booth

Yanıtlar:


322

Bu özel örnekte @Tagir% 100 doğru olduğunu düşünüyorum bir filtre içine almak ve iki kontrol yapın. Optional.ofNullableIsteğe bağlı şeyler kullanmak olmazdı gerçekten dönüş türleri için mantık yapmıyorum ... ama gerçekten ne burada ne de orada.

java.util.ObjectsGeniş bir durumda bunun için güzel bir yöntem olduğunu belirtmek istedim , böylece bunu yapabilirsiniz:

cars.stream()
    .filter(Objects::nonNull)

Boş nesneleri temizleyecek. Tanıdık olmayanlar için, bu aşağıdakilerin kısa elidir:

cars.stream()
    .filter(car -> Objects.nonNull(car))

Eldeki soruya kısmen cevap vermek için ile başlayan araç isimleri listesine dönün "M":

cars.stream()
    .filter(car -> Objects.nonNull(car))
    .map(car -> car.getName())
    .filter(carName -> Objects.nonNull(carName))
    .filter(carName -> carName.startsWith("M"))
    .collect(Collectors.toList());

Steno lambdalara alıştıktan sonra bunu da yapabilirsiniz:

cars.stream()
    .filter(Objects::nonNull)
    .map(Car::getName)        // Assume the class name for car is Car
    .filter(Objects::nonNull)
    .filter(carName -> carName.startsWith("M"))
    .collect(Collectors.toList());

Ne yazık ki bir kez .map(Car::getName)sadece isimleri listesini iade edeceksiniz, arabalar değil. Çok daha az güzel ama soruyu tamamen cevaplıyor:

cars.stream()
    .filter(car -> Objects.nonNull(car))
    .filter(car -> Objects.nonNull(car.getName()))
    .filter(car -> car.getName().startsWith("M"))
    .collect(Collectors.toList());

1
boş aracın sorun olmadığını unutmayın. Bu durumda, name özelliği sorunlara neden olur. Yani Objects::nonNullburada kullanılamaz ve son tavsiye cars.stream() .filter(car -> Objects.nonNull(car.getName()))inanıyorum olmalı
kiedysktos

1
BTW, cars.stream() .filter(car -> Objects.nonNull(car.getName()) && car.getName().startsWith("M"))bu soru bağlamında tavsiyelerinizin özeti olacağını düşünüyorum
kiedysktos

3
@kiedysktos Bu, çağırma işleminin .startWithde boş göstericiye neden olabileceği iyi bir nokta . Yapmaya çalıştığım nokta Java'nın özellikle boş nesneleri akışlarınızdan filtrelemek için bir yöntem sağlaması.
xbakesx

@Mark Booth evet, tabii ki Objects.nonNulleşdeğer != null, seçeneğiniz daha kısa
kiedysktos

1
StringCars ( Car) yerine araba isimleri ( ) listesi oluşturmuyor musunuz ?
user1803551

59

Sadece nulladı olan arabaları filtrelemeniz gerekir :

requiredCars = cars.stream()
                   .filter(c -> c.getName() != null)
                   .filter(c -> c.getName().startsWith("M"));

3
Soruyu cevaplayan tek cevap gibi göründüğü için bu cevabın daha yüksek oy kullanmaması çok yazık.
Mark Booth

@MarkBooth "Boş değerler nasıl filtrelenir?" xbakesx tarafından iyi cevaplanmış gibi görünüyor.
vegemite4me

@MarkBooth Doğru tarihlere bakarak. Benim hatam.
vegemite4me

Performans akıllıca akışı iki kez filtrelemek iyi mi yoksa filtreleme için yüklemi daha iyi mi kullanmalısınız? Sadece bilmek istiyorum.
Vaibhav_Sharma

51

Önerilen cevaplar harika. Sadece kullanarak boş listesinin şekilde ele almayan bir iyileşme önermek istiyorum Optional.ofNullable, Java 8'de yeni özelliği :

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

Yani, tam cevap:

 List<String> carsFiltered = Optional.ofNullable(cars)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull) //filtering car object that are null
                .map(Car::getName) //now it's a stream of Strings
                .filter(Objects::nonNull) //filtering null in Strings
                .filter(name -> name.startsWith("M"))
                .collect(Collectors.toList()); //back to List of Strings

5
İsteğe bağlı kötü kullanım. null hiçbir zaman boş bir Koleksiyon ile eşanlamlı olarak kullanılmamalıdır.
VGR

4
@VGR Tabii ki, ama pratikte olan bu değil. Bazen (çoğu zaman) birçok insanın üzerinde çalıştığı kodla çalışmanız gerekir. Bazen verilerinizi harici arayüzlerden alırsınız. Tüm bu durumlar için, İsteğe bağlı büyük bir kullanımdır.
Johnny

1
boş aracın sorun olmadığını unutmayın. Bu durumda, name özelliği sorunlara neden olur. Yani Objects::nonNullboş olmayan araç == ada sahip boş çünkü sorunu çözmezse
kiedysktos

Elbette @kiedysktos, ama cevapta göstermek istediğim şey bu değil. Ama söylediklerini kabul ediyorum ve cevabı düzenliyorum :)
Johnny

24

Bunu tek filtre adımında yapabilirsiniz:

requiredCars = cars.stream().filter(c -> c.getName() != null && c.getName().startsWith("M"));

getName()Birkaç kez aramak istemiyorsanız (örneğin, pahalı bir aramadır), bunu yapabilirsiniz:

requiredCars = cars.stream().filter(c -> {
    String name = c.getName();
    return name != null && name.startsWith("M");
});

Veya daha sofistike bir şekilde:

requiredCars = cars.stream().filter(c -> 
    Optional.ofNullable(c.getName()).filter(name -> name.startsWith("M")).isPresent());

İkinci örnekteki satır içi genişleme, kullanım durumum için değerliydi
Paul

3

Gücünden yararlanma java.util.Optional#map():

List<Car> requiredCars = cars.stream()
  .filter (car -> 
    Optional.ofNullable(car)
      .map(Car::getName)
      .map(name -> name.startsWith("M"))
      .orElse(false) // what to do if either car or getName() yields null? false will filter out the element
    )
  .collect(Collectors.toList())
;

1

bunu kullanabilirsin

List<Car> requiredCars = cars.stream()
    .filter (t->  t!= null && StringUtils.startsWith(t.getName(),"M"))
    .collect(Collectors.toList());
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.