Java8에서 람다를 사용하여 null이 아닌 경우에만 값 필터링


160

라고하는 객체 목록이 있습니다 car. Java 8을 사용하여 일부 매개 변수를 기준 으로이 목록을 필터링하고 싶습니다.하지만 매개 변수가 null이면 throw NullPointerException됩니다. null 값을 필터링하는 방법은 무엇입니까?

현재 코드는 다음과 같습니다

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

이 발생 NullPointerException하는 경우 getName()반환 null.


"널값이 아닌 경우에만 값을 필터링"또는 "널값을 필터링"하고 싶습니까? 나에게 모순되는 소리.
Holger

3
Tunaki의 답변 이 실제로 귀하의 질문에 대한 유일한 답변 인 것처럼 받아 들일 것을 제안 할 수 있습니까 ?
마크 부스

답변:


322

이 특정 예제에서 @Tagir는 100 % 정확하다고 생각합니다. 하나의 필터로 가져 와서 두 가지 검사를 수행하십시오. Optional.ofNullable선택 사항을 사용하지 않는 것은 실제로 반환 유형이 논리를 수행하지 않는 것입니다 ... 그러나 실제로 여기도 거기도 없습니다.

java.util.Objects광범위한 경우에 좋은 방법이 있다는 것을 지적하고 싶기 때문에 다음과 같이 할 수 있습니다.

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

null 객체가 지워집니다. 익숙하지 않은 사람에게는 다음과 같은 속기입니다.

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

다음으로 시작하는 자동차 이름 목록을 반환하기 위해 해당 질문에 부분적으로 대답하려면 다음을 수행하십시오 "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());

속기 람다에 익숙해지면 다음과 같이 할 수도 있습니다.

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

불행히도 일단 당신 .map(Car::getName)은 자동차가 아닌 이름의 목록 만 반환합니다. 그래서 아름답지는 않지만 질문에 완전히 대답합니다.

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

1
null 차량은 문제가되지 않습니다. 이 경우 이름 속성으로 인해 문제가 발생합니다. 그래서 Objects::nonNull여기에 사용될 수 없습니다, 그리고 마지막 조언에서 그것은 cars.stream() .filter(car -> Objects.nonNull(car.getName()))믿어야합니다
kiedysktos

1
BTW, 나는 cars.stream() .filter(car -> Objects.nonNull(car.getName()) && car.getName().startsWith("M"))이 질문 맥락에서 당신의 조언을 요약 한 것 같아요
kiedysktos

3
@kiedysktos 호출 .startWith하면 널 포인터가 발생할 수 있다는 점이 좋습니다 . 내가하려고했던 요점은 Java가 스트림에서 null 객체를 필터링하기위한 방법을 제공한다는 것입니다.
xbakesx

@ 마크 부스 예, 분명히 Objects.nonNull에 해당 != null사용자의 옵션은 짧은,
kiedysktos

1
자동차 이름 ( String) 대신 자동차 ( Car) 목록을 작성하지 않습니까?
user1803551

59

null이름 이있는 자동차 만 필터링하면됩니다 .

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

3
이 답변이 실제로 질문에 대답하는 유일한 답변 인 것처럼 보이기 때문에이 답변이 더 많이 투표되지 않는 것은 부끄러운 일입니다.
마크 부스

@MarkBooth 질문 "널값을 필터링하는 방법?" xbakesx가 잘 대답 한 것 같습니다.
vegemite4me

@MarkBooth 정확한 날짜를 확인하십시오. 내 실수.
vegemite4me

성능 현명하게 스트림을 두 번 필터링하는 것이 좋거나 필터링에 술어를 더 잘 사용합니까? 알고 싶어
Vaibhav_Sharma 5

51

제안 된 답변은 훌륭합니다. 그냥 사용하여 널리스트의 경우를 처리하기 위해 개선을 제안하고자 Optional.ofNullable, 자바 8의 새로운 기능 :

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

따라서 전체 답변은 다음과 같습니다.

 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
선택적 사용이 잘못되었습니다. 빈 컬렉션을 처음으로 동의어로 사용해서는 안됩니다.
VGR

4
@VGR 물론, 실제로는 그렇지 않습니다. 때때로 (대부분의 경우) 많은 사람들이 작업 한 코드로 작업해야합니다. 때때로 외부 인터페이스에서 데이터를받습니다. 이러한 모든 경우에 Optional은 매우 유용합니다.
Johnny

1
null 차량은 문제가되지 않습니다. 이 경우 이름 속성으로 인해 문제가 발생합니다. 그래서 Objects::nonNullnull 이외의 자동차가 == 이름을 널 수 있기 때문에 문제가 해결되지 않습니다
kiedysktos

물론 @kiedysktos이지만 대답에 표시하고 싶지는 않습니다. 그러나, 나는 당신이 말하는 것을 받아들이고 답을 편집하고 있습니다 :)
Johnny

24

단일 필터 단계에서이 작업을 수행 할 수 있습니다.

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

getName()여러 번 전화하고 싶지 않은 경우 (예 : 비싼 통화) 다음과 같이하면됩니다.

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

또는 더 복잡한 방식으로 :

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

두 번째 예의 인라인 확장은 내 사용 사례에 유용했습니다
Paul

3

의 힘을 활용 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

이것을 사용할 수 있습니다

List<Car> requiredCars = cars.stream()
    .filter (t->  t!= null && StringUtils.startsWith(t.getName(),"M"))
    .collect(Collectors.toList());
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.