Post

Java Stream Filter Lambda

1. Stream.filter() 사용

filter() 메소드는 주어진 Predicate와 일치하는 스트림 요소를 필터링할 수 있는 Stream 인터페이스의 중간 작업이다.

1
Stream<T> filter(Predicate<? super T> predicate)

어떻게 작동하는지 확인하기 위해 Customer 클래스를 만든다.

1
2
3
4
5
public class Customer {
    private String name;
    private int points;
    //Constructor and standard getters
}

또한 customers 컬렉션을 만든다.

1
2
3
4
5
6
Customer john = new Customer("John P.", 15);
Customer sarah = new Customer("Sarah M.", 200);
Customer charles = new Customer("Charles B.", 150);
Customer mary = new Customer("Mary T.", 1);

List<Customer> customers = Arrays.asList(john, sarah, charles, mary);

1) 컬렉션 필터링

filter() 메서드의 일반적인 사용 사례는 컬렉션을 처리하는 것이다.

포인트가 100점 이상인 고객 목록을 만든다. 이를 위해 람다 표현식을 사용할 수 있다.

1
2
3
4
List<Customer> customersWithMoreThan100Points = customers
  .stream()
  .filter(c -> c.getPoints() > 100)
  .collect(Collectors.toList());

람다 표현식의 약어인 메소드 참조를 사용할 수도 있다.

1
2
3
4
List<Customer> customersWithMoreThan100Points = customers
  .stream()
  .filter(Customer::hasOverHundredPoints)
  .collect(Collectors.toList());

이 경우 Customer 클래스에 hasOverHundredPoints 메서드를 추가했다.

1
2
3
public boolean hasOverHundredPoints() {
    return this.points > 100;
}

두 경우 모두 동일한 결과를 얻는다.

1
2
assertThat(customersWithMoreThan100Points).hasSize(2);
assertThat(customersWithMoreThan100Points).contains(sarah, charles);

2) 여러 기준으로 컬렉션 필터링

또한 filter()와 함께 여러 조건을 사용할 수 있다. 예를 들어 포인트와 이름으로 필터링할 수 있다.

1
2
3
4
5
6
7
List<Customer> charlesWithMoreThan100Points = customers
  .stream()
  .filter(c -> c.getPoints() > 100 && c.getName().startsWith("Charles"))
  .collect(Collectors.toList());

assertThat(charlesWithMoreThan100Points).hasSize(1);
assertThat(charlesWithMoreThan100Points).contains(charles);

2. 예외 처리

지금까지 예외를 발생시키지 않는 조건자와 함께 필터를 사용해 왔다. 실제로 Java의 기능적 인터페이스는 확인된 예외나 확인되지 않은 예외를 선언하지 않는다.

람다 식에서 예외를 처리하는 몇 가지 다른 방법이다.

1) 사용자 정의 래퍼 사용

먼저 Customer에 profilePhotoUrl을 추가한다.

1
private String profilePhotoUrl;

또한 프로필의 가용성을 확인하기 위해 간단한 hasValidProfilePhoto() 메서드를 추가한다.

1
2
3
4
5
public boolean hasValidProfilePhoto() throws IOException {
    URL url = new URL(this.profilePhotoUrl);
    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
    return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
}

hasValidProfilePhoto() 메서드가 IOException을 발생시키는 것을 볼 수 있다. 이 방법으로 고객을 필터링하려고 하면 다음과 같다.

1
2
3
4
List<Customer> customersWithValidProfilePhoto = customers
  .stream()
  .filter(Customer::hasValidProfilePhoto)
  .collect(Collectors.toList());

다음 오류가 표시된다.

1
Incompatible thrown types java.io.IOException in functional expression

이를 처리하기 위해 사용할 수 있는 대안 중 하나는 try-catch 블록으로 래핑하는 것이다.

1
2
3
4
5
6
7
8
9
10
11
List<Customer> customersWithValidProfilePhoto = customers
  .stream()
  .filter(c -> {
      try {
          return c.hasValidProfilePhoto();
      } catch (IOException e) {
          //handle exception
      }
      return false;
  })
  .collect(Collectors.toList());

조건자에서 예외를 발생시켜야 하는 경우 RuntimeException과 같은 확인되지 않은 예외로 래핑할 수 있다.

2) Throwing 함수 사용

ThrowingFunction 라이브러리를 사용할 수 있다.

ThrowingFunction은 Java 기능 인터페이스에서 확인된 예외를 처리할 수 있는 오픈 소스 라이브러리이다.

pom에 throwing-function 종속성을 추가한다.

1
2
3
4
5
<dependency>
    <groupId>com.pivovarit</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.5.1</version>
</dependency>

조건자의 예외를 처리하기 위해 이 라이브러리는 검사된 예외를 래핑하는 unchecked() 메서드가 있는 ThrowingPredicate 클래스를 제공한다.

실제로 살펴본다.

1
2
3
4
List customersWithValidProfilePhoto = customers
  .stream()
  .filter(ThrowingPredicate.unchecked(Customer::hasValidProfilePhoto))
  .collect(Collectors.toList());

[출처 및 참고]

This post is licensed under CC BY 4.0 by the author.