プログラミング

Java Stream API: filterを使ったデータフィルタリング完全ガイド

この記事は約9分で読めます。

はじめに

Stream APIと条件でのフィルタを行うfilter

JavaのStream APIは、コレクション・フィルタ・マップ・リデュースなどの操作を簡単に行える強力な機能です。この中でもfilterは、条件に合致する要素を抽出するための中間操作に活用されます。この記事では、Stream APIのfilterのサンプルコードを紹介しながら、基本概念について記載します。

基本的なfilterの使用例

単一条件を指定する

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ComplexFilterExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Ai", "Bob", "Charlie", "David");

        // 名前が 'A' で始まる
        List<String> filteredNames = names.stream()
                                          .filter(name -> name.startsWith("A"))
                                          .collect(Collectors.toList());

        System.out.println("Filtered Names: " + filteredNames);
        // 実行結果:
        // Filtered Names: [Alice, Ai]
    }
}

filterメソッドは単一条件だけでなく、複数の条件を組み合わせて使用することもできます。次にその例を見てみましょう。

複数の条件を指定する

filter は、複数の条件を組み合わせることも可能です。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ComplexFilterExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Ai", "Bob", "Charlie", "David");

        // 名前が 'A' または 'B' で始まり、長さが 3 文字以上
        List<String> filteredNames = names.stream()
                .filter(name -> (name.startsWith("A") || name.startsWith("B")))
                .filter(name -> name.length() > 3)
                .collect(Collectors.toList());

        System.out.println("Filtered Names: " + filteredNames);
        // 実行結果:
        // Filtered Names: [Alice, Bob]
    }
}

複数のfilterをチェインして使用することで、さらに精密な条件を指定できます。filterをチェインするのはand条件となるため、3文字以下のものが対象外となっています。or条件としたい場合は、filterをチェインする方法では実現できないため、1つのfilter内にor条件を書きます。

実用的なfilterの使用例

実用情報: 人物オブジェクトのフィルタリング

次の例は、クラスのリストから年齢と性別に基づいてフィルタする方法です。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

class Person {
    String name;
    int age;
    String gender;

    Person(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public boolean isMale() {
        return "male".equals(gender);
    }

    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

public class PersonFilterExample {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 30, "female"),
            new Person("Bob", 20, "male"),
            new Person("Charlie", 35, "male"),
            new Person("David", 25, "male")
        );

        // 30歳以上の男性を抽出
        List<Person> filteredPeople = people.stream()
                .filter(person -> person.age >= 30)
            .filter(person::isMale)
                .collect(Collectors.toList());

        System.out.println("Filtered People: " + filteredPeople);
        // 実行結果:
        // Filtered People: [Charlie (35)]
    }
}

このように、オブジェクトの属性やメソッドを基にしたフィルタリングも可能です。メソッドを元にしたフィルタリングについては、続きの基本概念についてで理解を深めましょう。

filter の基本概念

filter は Stream API のメソッドの一つで、Predicate (予想を表す関数型インターフェース) を参照として受け取り、それに合致する要素のみを含む Stream を返します。

Predicateとは

Predicateは、たった1つの基本メソッドtest を持つ関数型インターフェースです。このtest メソッドは、引数を受け取り、その引数が指定の条件を満たしているかどうかを返します。これは普通はラムダの関数定義を使って表現されます。

以下は基本的なPredicateの例です。

Predicate<Integer> isOdd = n -> n % 2 != 0;
System.out.println(isOdd.test(3)); // true
System.out.println(isOdd.test(4)); // false

詳しくは、関数型インターフェースに関する記事を確認してみてください!

filter で Predicate を利用する例

以下の例は、奇数のみを抽出する方法をPredicateを使って実現したものです。

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class PredicateExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        // Predicateを定義
        Predicate<Integer> isOdd = n -> n % 2 != 0;

        // 奇数のみを抽出
        List<Integer> oddNumbers = numbers.stream()
                .filter(isOdd)
                .collect(Collectors.toList());

        System.out.println("Odd Numbers: " + oddNumbers);
        // 実行結果:
        // Odd Numbers: [1, 3, 5]
    }
}

Predicateを別作することで、同じ条件を複数の場所で再利用できる為、コードの再利用性を高めることができます。Predicateを作らずとも、booleanを返却するメソッドを用意しそれを利用することもできます。

ラムダ式のメソッド参照を利用する例

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class PredicateExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        // 奇数のみを抽出
        List<Integer> oddNumbers = numbers.stream()
                .filter(PredicateExample::isOdd)
                .collect(Collectors.toList());

        System.out.println("Odd Numbers: " + oddNumbers);
    }

  public static boolean isOdd(Integer number) {
        return number % 2 != 0;
    }
}

まとめ

  • filterはStream APIの中間操作で、条件に合致する要素を抽出します。
  • Predicateは条件を定義するための便利なインターフェースです。
  • 複数条件の組み合わせやラムダ式・メソッド参照を活用することで、柔軟で簡潔なコードが書けます。 実際のプロジェクトに適用して、データ操作を効率化してみましょう!

Java Stream API の filter は、簡単な文法で持ち運びのデータをフィルタリングするために非常に有用です。基本的な使い方から複雑な条件まで、この記事で説明した例を参考にして、自分のコードに適用してみてください。