ラムダ式のJavaストリームフィルタ

1前書き

このクイックチュートリアルでは、https://www.baeldung.com/java-8-streams-introduction[ Streams [Java]を使用して作業するときの Stream.filter() メソッドの使用方法について説明します。

それを使用する方法と、チェックされた例外がある特別なケースを処理する方法を示します。

2 Stream.filter() を使う

filter() メソッドは Stream インターフェースの中間的な操作で、与えられた__Predicateにマッチするストリームの要素をフィルターすることができます

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

これがどのように機能するかを見るために、 Customer クラスを作成しましょう:

public class Customer {
    private String name;
    private int points;
   //Constructor and standard getters
}

さらに、顧客のコレクションを作成しましょう。

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

2.1. コレクションのフィルタリング

filter() メソッドの一般的なユースケースはhttps://www.baeldung.com/java-collection-filtering[processing collection]です。

100以上の__点を持つ顧客のリストを作成しましょう。それを行うには、ラムダ式を使用します。

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

method reference を使用することもできます。これはラムダ式の省略形です。

List<Customer> customersWithMoreThan100Points = customers
  .stream()
  .filter(Customer::hasOverThousandPoints)
  .collect(Collectors.toList());

しかし、この場合は hasOverThousandPoints メソッドを Customer クラスに追加しました。

public boolean hasOverThousandPoints() {
    return this.points > 100;
}

どちらの場合も、同じ結果が得られます。

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

2.2. 複数の基準によるコレクションのフィルタリング

また、 filter() では複数の条件を使用できます。たとえば、 points name でフィルタリングします。

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

3例外処理

今までは、例外をスローしない述語とともにフィルタを使用していました。実際、Javaの 機能的インタフェースは、チェック済みまたは未チェックの例外を宣言していません

次に、https://www.baeldung.com/java-lambda-exceptions[ラムダ式の例外]を処理する方法をいくつか紹介します。

3.1. カスタムラッパーの使用

まず、 Customer に__profilePhotoUrlを追加します。

private String profilePhotoUrl;

さらに、プロファイルの可用性を確認するための簡単な hasValidProfilePhoto() メソッドを追加しましょう。

public boolean hasValidProfilePhoto() throws IOException {
    URL url = new URL(this.profilePhotoUrl);
    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
    return connection.getResponseCode() == HttpURLConnection.HTTP__OK;
}

hasValidProfilePhoto() メソッドが IOException をスローすることがわかります。さて、この方法で顧客をフィルタリングしようとすると:

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

次のようなエラーが表示されます。

Incompatible thrown types java.io.IOException in functional expression

それを処理するために、私たちが使用できる代替手段の1つはtry-catchブロックでそれをラップすることです。

List<Customer> customersWithValidProfilePhoto = customers
  .stream()
  .filter(c -> {
      try {
          return c.hasValidProfilePhoto();
      } catch (IOException e) {
         //handle exception
      }
      return false;
  })
  .collect(Collectors.toList());

述語から例外をスローする必要がある場合は、 RuntimeException のような未チェックの例外でラップすることができます。

3.2. ThrowingFunction を使う

あるいは、 ThrowingFunction ライブラリを使用することもできます。

ThrowingFunctionはオープンソースのライブラリで、これを使ってJavaの関数型インタフェースでチェック済み例外を処理できます。

throwing-function依存関係 をpomに追加することから始めましょう。

<dependency>
    <groupId>pl.touk</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.3</version>
</dependency>

述部の例外を処理するために、このライブラリーは ThrowingPredicate クラスを提供しています。このクラスには、チェック済みの例外をラップする unchecked() メソッドがあります。

実際に見てみましょう。

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

4結論

この記事では、 filter() メソッドを使用してストリームを処理する方法の例を説明しました。また、例外を処理するためのいくつかの代替方法を検討しました。

いつものように、完全なコードはhttps://github.com/earth001/tutorials/tree/master/java-streams[over on GitHub]から入手できます。