Javaプログラミングにおいて、ラムダ式は簡潔で表現力のあるコードを書くための強力なツールです。その中でも、Predicate
は関数型インターフェースの一つであり、条件を評価して真偽値を返すためのメソッドを提供します。特に、Predicate
とラムダ式を組み合わせることで、コードの可読性や再利用性が向上し、複雑な条件処理を簡潔に表現することが可能になります。本記事では、Javaのラムダ式とPredicate
の基本概念から、その実装や応用例までを解説し、実際の開発現場でどのように活用できるかを詳しく紹介します。これにより、Javaプログラムでより効果的に条件処理を行えるようになります。
Predicateインターフェースとは
Predicate
インターフェースは、Javaの標準ライブラリに含まれる関数型インターフェースで、単一の抽象メソッドtest
を持ちます。このtest
メソッドは、引数として与えられたオブジェクトに対して、条件を評価し、真(true
)または偽(false
)を返します。Predicate
は、フィルタリングや条件付き処理など、多くの用途で利用され、特にストリームAPIと組み合わせて使用されることが多いです。
Predicateの基本構造
Predicate
インターフェースの基本構造は非常にシンプルです。以下はその定義です。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
このインターフェースを実装することで、条件を評価するロジックをカプセル化し、再利用可能なコードとして活用できます。たとえば、Predicate<String>
を使って文字列が空でないかをチェックすることができます。
基本的な使用例
以下は、Predicate
インターフェースを使って、整数が偶数であるかどうかを判定する簡単な例です。
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // 出力: true
System.out.println(isEven.test(5)); // 出力: false
このように、Predicate
を使うことで、条件評価を簡潔に表現できるようになります。
ラムダ式を使用したPredicateの実装
Javaのラムダ式を使うことで、Predicate
インターフェースをより簡潔に実装することができます。ラムダ式は、匿名関数として振る舞い、通常のメソッドよりも少ないコードで処理を記述することが可能です。Predicate
のtest
メソッドに対応するラムダ式を用いることで、条件を評価するコードを短く、かつ読みやすくできます。
ラムダ式でのPredicateの記述方法
ラムダ式を使用してPredicate
を実装する際の基本構文は以下の通りです。
Predicate<T> predicate = (T t) -> { 条件式; };
ここで、T
は任意の型を指し、条件式
の部分には評価したいロジックを記述します。例えば、文字列が空でないかを判定するPredicate
をラムダ式で実装する場合、以下のようになります。
Predicate<String> isNotEmpty = str -> str != null && !str.isEmpty();
このisNotEmpty
は、文字列が空でないかどうかを評価し、true
またはfalse
を返します。
具体例: 数値の判定
以下に、数値が正の値かどうかを判定するPredicate
の例を示します。
Predicate<Integer> isPositive = n -> n > 0;
このisPositive
は、引数として与えられた整数が正の値であればtrue
を返し、そうでなければfalse
を返します。実際に使用する際は、以下のように簡単に呼び出せます。
System.out.println(isPositive.test(10)); // 出力: true
System.out.println(isPositive.test(-5)); // 出力: false
複雑な条件の実装
ラムダ式を用いることで、複雑な条件も簡潔に記述することができます。例えば、数値が正であり、かつ偶数であるかどうかを判定するPredicate
を実装する場合、次のように記述できます。
Predicate<Integer> isPositiveAndEven = n -> n > 0 && n % 2 == 0;
このように、ラムダ式を活用することで、条件に応じたPredicate
を容易に作成でき、コードの可読性と保守性を向上させることができます。
Predicateのチェーン利用
JavaのPredicate
インターフェースには、複数の条件を連鎖的に結合するためのメソッドが用意されています。これにより、単一のPredicate
で表現できる条件が複数に分かれている場合でも、簡潔に組み合わせて複雑なロジックを構築することが可能です。代表的なメソッドには、and
、or
、negate
があります。
andメソッドでの条件の結合
and
メソッドは、二つのPredicate
を結合し、両方の条件が満たされた場合にのみtrue
を返します。例えば、数値が正の数であり、かつ偶数であるかを判定する場合、以下のように記述できます。
Predicate<Integer> isPositive = n -> n > 0;
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> isPositiveAndEven = isPositive.and(isEven);
このisPositiveAndEven
は、数値が正の数であり、かつ偶数である場合にtrue
を返します。
System.out.println(isPositiveAndEven.test(4)); // 出力: true
System.out.println(isPositiveAndEven.test(3)); // 出力: false
System.out.println(isPositiveAndEven.test(-2)); // 出力: false
orメソッドでの条件の結合
or
メソッドは、二つのPredicate
を結合し、どちらか一方の条件が満たされればtrue
を返します。例えば、数値が負の数であるか、偶数であるかを判定する場合、以下のように記述できます。
Predicate<Integer> isNegative = n -> n < 0;
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> isNegativeOrEven = isNegative.or(isEven);
このisNegativeOrEven
は、数値が負の数であるか、偶数である場合にtrue
を返します。
System.out.println(isNegativeOrEven.test(-3)); // 出力: true
System.out.println(isNegativeOrEven.test(2)); // 出力: true
System.out.println(isNegativeOrEven.test(3)); // 出力: false
negateメソッドでの条件の否定
negate
メソッドは、指定したPredicate
の結果を否定します。つまり、元のPredicate
がtrue
を返す場合にfalse
を返し、その逆も然りです。例えば、数値が偶数でないかどうかを判定する場合、以下のように記述できます。
Predicate<Integer> isOdd = isEven.negate();
このisOdd
は、数値が奇数である場合にtrue
を返します。
System.out.println(isOdd.test(4)); // 出力: false
System.out.println(isOdd.test(3)); // 出力: true
複数の条件を組み合わせた複雑なロジック
これらのメソッドを組み合わせることで、複雑な条件を簡潔に表現することができます。例えば、数値が正であり、かつ偶数でない(奇数である)かどうかを判定する場合、以下のように記述できます。
Predicate<Integer> isPositiveAndOdd = isPositive.and(isEven.negate());
このようにして作成したPredicate
を使えば、複数の条件を効率的にチェックすることが可能となります。チェーン利用により、コードの可読性を保ちながら、柔軟で強力な条件処理を実装することができます。
Predicateの応用例: フィルタリング
Predicate
を使用する最も一般的な応用例の一つが、リストやコレクション内のデータをフィルタリングすることです。JavaのストリームAPIと組み合わせることで、条件に合致する要素を効率的に抽出し、より直感的で簡潔なコードを実現できます。
リストのフィルタリング
例えば、整数のリストから偶数のみを抽出する場合、Predicate
を使用して次のように実装できます。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Predicate<Integer> isEven = n -> n % 2 == 0;
List<Integer> evenNumbers = numbers.stream()
.filter(isEven)
.collect(Collectors.toList());
System.out.println(evenNumbers); // 出力: [2, 4, 6, 8, 10]
このコードでは、filter
メソッドを用いて、リストの各要素に対してPredicate
が評価され、true
を返す要素のみが結果のリストに含まれます。
複数条件のフィルタリング
Predicate
のチェーン利用により、複数の条件に基づいてリストをフィルタリングすることも可能です。例えば、正の偶数のみをリストから抽出する場合は、以下のように記述できます。
Predicate<Integer> isPositive = n -> n > 0;
Predicate<Integer> isPositiveAndEven = isPositive.and(isEven);
List<Integer> positiveEvenNumbers = numbers.stream()
.filter(isPositiveAndEven)
.collect(Collectors.toList());
System.out.println(positiveEvenNumbers); // 出力: [2, 4, 6, 8, 10]
このように、複数の条件を組み合わせることで、必要なデータのみを効率的に抽出することができます。
文字列リストのフィルタリング
Predicate
は数値だけでなく、文字列や他のオブジェクトにも適用できます。例えば、文字列リストから特定の文字列が含まれている要素を抽出する場合、次のように実装できます。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Edward");
Predicate<String> startsWithA = str -> str.startsWith("A");
List<String> namesStartingWithA = names.stream()
.filter(startsWithA)
.collect(Collectors.toList());
System.out.println(namesStartingWithA); // 出力: [Alice]
この例では、startsWithA
というPredicate
を使って、文字列が”A”で始まるかどうかをチェックし、その条件に合致する要素だけを抽出しています。
実世界でのフィルタリング例
実際のアプリケーション開発では、Predicate
を使ったフィルタリングは非常に役立ちます。例えば、ユーザー管理システムで特定の条件に基づいてユーザーをフィルタリングする、商品リストから特定の価格帯やカテゴリーの商品を抽出するなど、多くの場面で活用できます。
以下は、商品リストから価格が1000円以上で、在庫がある商品を抽出する例です。
List<Product> products = getProductList(); // 仮のメソッドで商品リストを取得
Predicate<Product> isExpensive = p -> p.getPrice() >= 1000;
Predicate<Product> isInStock = p -> p.isInStock();
List<Product> filteredProducts = products.stream()
.filter(isExpensive.and(isInStock))
.collect(Collectors.toList());
このコードでは、価格が1000円以上でかつ在庫がある商品だけをリストから抽出しています。このように、Predicate
とストリームAPIを組み合わせることで、データのフィルタリング処理を簡潔に行うことができます。
カスタムPredicateの作成方法
標準的なPredicate
だけではカバーしきれない複雑な条件や、特定のビジネスロジックに基づいた条件判定を行いたい場合、カスタムPredicate
を作成することで柔軟に対応できます。カスタムPredicate
は、特定の条件に基づくロジックを再利用可能な形でカプセル化するのに最適です。
カスタムPredicateの基本的な作成方法
カスタムPredicate
を作成するには、Predicate
インターフェースをラムダ式やメソッド参照で実装します。例えば、カスタム条件として、文字列の長さが5文字以上かどうかを判定するPredicate
を作成する場合、次のように記述できます。
Predicate<String> isLengthGreaterThanFive = str -> str.length() > 5;
このisLengthGreaterThanFive
は、与えられた文字列が5文字以上であればtrue
を返します。
System.out.println(isLengthGreaterThanFive.test("Hello")); // 出力: false
System.out.println(isLengthGreaterThanFive.test("Welcome")); // 出力: true
メソッド参照によるカスタムPredicateの作成
より複雑なカスタムPredicate
を作成する場合、ラムダ式だけでなく、メソッド参照を使うことも可能です。例えば、特定の条件に合致する文字列を判定するメソッドを別途定義し、それをPredicate
として使用することができます。
public class StringUtils {
public static boolean isAllUpperCase(String str) {
return str.equals(str.toUpperCase());
}
}
Predicate<String> isUpperCase = StringUtils::isAllUpperCase;
このisUpperCase
は、文字列がすべて大文字で構成されている場合にtrue
を返します。
System.out.println(isUpperCase.test("HELLO")); // 出力: true
System.out.println(isUpperCase.test("Hello")); // 出力: false
複雑なビジネスロジックを反映したカスタムPredicate
実際のプロジェクトでは、ビジネスロジックに基づく複雑な条件判定が必要になることがあります。例えば、顧客の購入履歴から特定の条件に基づいてロイヤルティプログラムの対象者を選定する場合、以下のようにカスタムPredicate
を作成することができます。
Predicate<Customer> isEligibleForLoyaltyProgram = customer ->
customer.getTotalPurchases() > 1000 && customer.getJoinDate().isBefore(LocalDate.now().minusYears(1));
このカスタムPredicate
は、顧客の購入総額が1000ドルを超え、かつ1年以上前に会員登録している場合にtrue
を返します。
Customer customer = new Customer(1500, LocalDate.of(2020, 5, 20));
System.out.println(isEligibleForLoyaltyProgram.test(customer)); // 出力: true
カスタムPredicateの再利用
カスタムPredicate
を作成することで、特定の条件判定ロジックをプロジェクト内で再利用することができます。例えば、複数の場所で同じ顧客フィルタリングロジックが必要な場合、一度定義したカスタムPredicate
を使い回すことができます。
List<Customer> customers = getCustomers();
List<Customer> eligibleCustomers = customers.stream()
.filter(isEligibleForLoyaltyProgram)
.collect(Collectors.toList());
このように、カスタムPredicate
を活用することで、複雑な条件判定を簡潔に表現し、コードの再利用性と保守性を高めることができます。カスタムPredicate
は、プロジェクト全体で一貫した条件判定ロジックを提供するために非常に有効です。
Predicateのパフォーマンス最適化
Predicate
を使用する際、特に大規模なデータセットや複雑な条件を扱う場合には、パフォーマンスの最適化が重要です。適切に最適化されたPredicate
は、コードの効率性を大幅に向上させるだけでなく、全体の処理時間を短縮し、リソースの消費を抑えることができます。
短絡評価を活用する
JavaのPredicate
は、条件を評価する際に短絡評価(ショートサーキット評価)を行います。短絡評価とは、論理演算子&&
や||
を用いた場合に、左側の条件だけで結果が決定される場合、右側の条件を評価しないという特性です。これを活用することで、不要な計算を省き、パフォーマンスを向上させることができます。
例えば、以下のようなPredicate
を考えます。
Predicate<String> isNonEmptyAndStartsWithA = str -> str != null && !str.isEmpty() && str.startsWith("A");
この例では、str
がnull
であれば、!str.isEmpty()
やstr.startsWith("A")
の評価は行われません。これにより、余計な評価を防ぎ、無駄なリソース消費を抑えることができます。
コレクション操作の最適化
ストリームAPIとPredicate
を組み合わせて大量のデータを処理する場合、コレクション操作を最適化することが重要です。特に、filter
メソッドで複数のPredicate
をチェーンする際には、最もコストの低い条件から順に評価することで、全体の処理速度を改善できます。
例えば、複数のPredicate
を組み合わせてリストをフィルタリングする場合、軽量な条件を先に評価するようにします。
Predicate<Employee> isSenior = emp -> emp.getYearsOfExperience() > 10;
Predicate<Employee> isHighSalary = emp -> emp.getSalary() > 100000;
List<Employee> seniorHighSalaryEmployees = employees.stream()
.filter(isSenior.and(isHighSalary))
.collect(Collectors.toList());
この場合、経験年数の評価が先に行われるため、少数の対象に絞り込まれ、給与の評価がより効率的に行われます。
ラムダキャプチャによるパフォーマンスへの影響
ラムダ式内で外部の変数(キャプチャ変数)を使用すると、ラムダキャプチャが発生します。キャプチャ変数は、ラムダ式が呼び出されるたびにアクセスされるため、パフォーマンスに影響を与える可能性があります。頻繁に使用されるPredicate
では、キャプチャを避けるか、必要最小限に留めることで、効率を向上させることができます。
int threshold = 10;
Predicate<Integer> isGreaterThanThreshold = n -> n > threshold; // キャプチャが発生
もしthreshold
が固定値である場合、次のように直接数値を使うことで、キャプチャを避けることができます。
Predicate<Integer> isGreaterThanTen = n -> n > 10;
プリミティブ型の使用による最適化
Javaでは、オートボクシングとアンボクシングによってプリミティブ型(int、doubleなど)がオブジェクト型(Integer、Doubleなど)に変換されます。この変換は処理にオーバーヘッドを伴うため、可能な限りプリミティブ型のPredicate
(IntPredicate
、DoublePredicate
など)を使用することで、パフォーマンスを向上させることができます。
IntPredicate isEven = n -> n % 2 == 0;
このようにプリミティブ型のPredicate
を使用することで、オートボクシングによるオーバーヘッドを回避し、処理速度を向上させることができます。
まとめと実践的なアドバイス
Predicate
のパフォーマンス最適化は、コードの効率性を大幅に向上させるための重要な要素です。短絡評価を活用し、条件の評価順序を最適化し、ラムダキャプチャを避け、プリミティブ型を使用することで、特に大規模データセットやリアルタイム処理において、顕著な効果を得ることができます。これらの最適化戦略を実践することで、より高速で効率的なJavaプログラムを構築できるでしょう。
PredicateとOptionalの組み合わせ
Java 8で導入されたOptional
クラスは、null
を扱う際のリスクを軽減するために設計されたクラスです。Optional
とPredicate
を組み合わせることで、より安全で柔軟なコードを書くことができます。特に、Optional
内に含まれる値が特定の条件を満たすかどうかを確認する際に、この組み合わせが非常に役立ちます。
OptionalとPredicateの基本的な組み合わせ方
Optional
とPredicate
を組み合わせる最も基本的な方法は、Optional
のfilter
メソッドを使用することです。このメソッドは、Predicate
を引数に取り、Optional
の値がそのPredicate
に一致する場合にはその値を含む新しいOptional
を返し、一致しない場合は空のOptional
を返します。
Optional<String> optionalName = Optional.of("Alice");
Optional<String> filteredName = optionalName.filter(name -> name.startsWith("A"));
System.out.println(filteredName); // 出力: Optional[Alice]
この例では、optionalName
が”A”で始まる場合のみ、filteredName
に値が保持されます。そうでない場合、filteredName
は空のOptional
になります。
複数の条件を組み合わせたOptionalのフィルタリング
Predicate
をチェーンすることで、複数の条件を使ってOptional
をフィルタリングすることも可能です。例えば、文字列が”A”で始まり、かつその長さが5文字以上であるかどうかをチェックする場合、次のように記述できます。
Optional<String> optionalName = Optional.of("Alice");
Predicate<String> startsWithA = name -> name.startsWith("A");
Predicate<String> lengthGreaterThanFive = name -> name.length() > 5;
Optional<String> filteredName = optionalName.filter(startsWithA.and(lengthGreaterThanFive));
System.out.println(filteredName); // 出力: Optional.empty
この例では、optionalName
の値が5文字未満であるため、結果として空のOptional
が返されます。
Optionalの空チェックとPredicateの組み合わせ
Optional
が空かどうかをチェックしつつ、特定の条件を満たすかどうかを確認するには、isPresent
メソッドとfilter
メソッドを組み合わせることが考えられます。
Optional<String> optionalName = Optional.ofNullable(null);
boolean isValidName = optionalName.filter(name -> name.length() > 3).isPresent();
System.out.println(isValidName); // 出力: false
この例では、optionalName
がnull
の場合、filter
メソッドは呼び出されず、直接false
が返されます。これにより、null
チェックと条件判定を1行で実現できます。
OptionalとカスタムPredicateの活用
カスタムPredicate
を使用して、特定のビジネスロジックに基づいた条件をOptional
に適用することも可能です。例えば、顧客オブジェクトがVIPであるかどうかを判定するカスタムPredicate
を作成し、それをOptional
と組み合わせて使用することができます。
class Customer {
private String name;
private boolean isVIP;
// コンストラクタ、ゲッターなど
public boolean isVIP() {
return isVIP;
}
}
Predicate<Customer> isVIPCustomer = Customer::isVIP;
Optional<Customer> customerOptional = Optional.of(new Customer("John", true));
Optional<Customer> vipCustomer = customerOptional.filter(isVIPCustomer);
System.out.println(vipCustomer.isPresent()); // 出力: true
この例では、Optional
に含まれる顧客がVIPである場合にのみ、その顧客オブジェクトがOptional
として保持されます。
OptionalとPredicateを組み合わせるメリット
Optional
とPredicate
を組み合わせることで、null
チェックと条件判定を安全かつ効率的に行うことができ、コードの可読性と保守性が向上します。また、Optional
を用いることで、意図しないNullPointerException
の発生を防ぐことができ、より堅牢なアプリケーションを構築することが可能です。
このようなテクニックを活用することで、Javaのコードをより直感的で安全にし、複雑な条件ロジックも簡潔に表現できるようになります。
実際の開発での利用シナリオ
Predicate
とラムダ式の組み合わせは、Javaプログラムにおいて幅広く活用されており、特にデータのフィルタリングや条件判定が必要な場面でその力を発揮します。ここでは、実際の開発現場でよく遭遇するシナリオにおいて、Predicate
がどのように役立つかを具体的に解説します。
ユーザー認証システムでの利用
ユーザー認証システムでは、ユーザーが特定の条件を満たしているかどうかを判定する必要があります。例えば、ユーザーがアクティブなアカウントを持ち、かつ特定の権限を有しているかをチェックする場合、以下のようにPredicate
を使用して効率的に実装できます。
class User {
private boolean isActive;
private Set<String> roles;
// コンストラクタ、ゲッターなど
public boolean isActive() {
return isActive;
}
public boolean hasRole(String role) {
return roles.contains(role);
}
}
Predicate<User> isActiveUser = User::isActive;
Predicate<User> isAdmin = user -> user.hasRole("ADMIN");
Predicate<User> isActiveAdmin = isActiveUser.and(isAdmin);
User user = new User(true, Set.of("ADMIN", "USER"));
if (isActiveAdmin.test(user)) {
System.out.println("User is an active admin.");
} else {
System.out.println("User is not an active admin.");
}
このシナリオでは、ユーザーがアクティブであり、かつ管理者権限を持っているかどうかを効率的にチェックできます。
商品フィルタリング機能の実装
ECサイトの開発では、ユーザーが特定の条件に基づいて商品をフィルタリングできる機能が重要です。例えば、在庫があり、価格が特定の範囲内の商品を表示する場合、以下のようにPredicate
を活用できます。
class Product {
private double price;
private boolean inStock;
// コンストラクタ、ゲッターなど
public double getPrice() {
return price;
}
public boolean isInStock() {
return inStock;
}
}
Predicate<Product> isAffordable = product -> product.getPrice() <= 1000;
Predicate<Product> isAvailable = Product::isInStock;
Predicate<Product> affordableAndAvailable = isAffordable.and(isAvailable);
List<Product> products = List.of(
new Product(800, true),
new Product(1200, true),
new Product(500, false)
);
List<Product> filteredProducts = products.stream()
.filter(affordableAndAvailable)
.collect(Collectors.toList());
filteredProducts.forEach(product -> System.out.println("Filtered product: " + product.getPrice()));
このコードは、価格が1000円以下で、かつ在庫がある商品だけを抽出するものです。商品フィルタリング機能は、ユーザー体験の向上に直結する重要な機能です。
ログデータの分析とフィルタリング
システムのログデータを分析する際には、特定の条件に合致するログエントリのみを抽出する必要があります。例えば、特定のエラーレベル(例えば、ERROR
やWARNING
)のログだけを抽出する場合、以下のようにPredicate
を活用できます。
class LogEntry {
private String level;
private String message;
// コンストラクタ、ゲッターなど
public String getLevel() {
return level;
}
}
Predicate<LogEntry> isErrorOrWarning = log -> log.getLevel().equals("ERROR") || log.getLevel().equals("WARNING");
List<LogEntry> logs = List.of(
new LogEntry("INFO", "System started"),
new LogEntry("ERROR", "Null pointer exception"),
new LogEntry("WARNING", "Deprecated API usage")
);
List<LogEntry> filteredLogs = logs.stream()
.filter(isErrorOrWarning)
.collect(Collectors.toList());
filteredLogs.forEach(log -> System.out.println("Filtered log: " + log.getMessage()));
この例では、ERROR
およびWARNING
レベルのログだけを抽出しています。これにより、重要なログメッセージにすばやくアクセスでき、迅速な対応が可能となります。
条件付きメール送信の実装
マーケティングオートメーションでは、特定の条件を満たすユーザーにのみメールを送信することが求められることが多いです。例えば、特定の購買行動を取ったユーザーや、一定の期間内にアクティブであったユーザーにのみメールを送信する場合、Predicate
を活用することで、対象ユーザーを効率的に選定できます。
Predicate<User> madePurchaseRecently = user -> user.getLastPurchaseDate().isAfter(LocalDate.now().minusDays(30));
Predicate<User> isEmailOptedIn = User::isEmailOptedIn;
Predicate<User> shouldSendEmail = madePurchaseRecently.and(isEmailOptedIn);
List<User> users = getUsers();
users.stream()
.filter(shouldSendEmail)
.forEach(user -> sendEmail(user));
このシナリオでは、過去30日以内に購入し、かつメールの受信を希望しているユーザーにのみメールを送信する条件を実装しています。
まとめ
これらのシナリオを通じて、Predicate
が実際の開発現場でいかに役立つかが理解できたでしょう。Predicate
を利用することで、条件付きロジックを簡潔に、かつ再利用可能な形で表現できるため、コードの保守性と拡張性が向上します。Predicate
の応用範囲は広く、データフィルタリングからユーザー認証、ログ分析まで、さまざまな場面で活用することができます。
演習問題: Predicateの実装練習
ここでは、Predicate
を使用したいくつかの演習問題を通じて、実装方法とその応用についてさらに理解を深めていきます。これらの練習問題を解くことで、Predicate
の利用方法を実践的に習得できるようになります。
演習1: 数値リストのフィルタリング
次の問題に取り組んでみてください。整数のリストが与えられたときに、偶数かつ10より大きい値のみを含むリストを作成してください。Predicate
を使用してフィルタリングを行いましょう。
List<Integer> numbers = Arrays.asList(3, 10, 15, 20, 25, 30);
// TODO: Predicateを使用して、偶数かつ10より大きい数のみを含むリストを作成
Predicate<Integer> isEvenAndGreaterThanTen = num -> num % 2 == 0 && num > 10;
List<Integer> result = numbers.stream()
.filter(isEvenAndGreaterThanTen)
.collect(Collectors.toList());
System.out.println(result); // 期待される出力: [20, 30]
演習2: 文字列のフィルタリング
次に、文字列リストから”e”を含み、かつその長さが5文字以上である文字列だけを抽出するPredicate
を作成してください。
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// TODO: Predicateを使用して、"e"を含みかつ5文字以上の文字列のみを抽出
Predicate<String> containsEAndLengthAtLeastFive = word -> word.contains("e") && word.length() >= 5;
List<String> filteredWords = words.stream()
.filter(containsEAndLengthAtLeastFive)
.collect(Collectors.toList());
System.out.println(filteredWords); // 期待される出力: ["apple", "elderberry"]
演習3: ユーザーオブジェクトのフィルタリング
最後に、次のユーザーオブジェクトのリストから、アクティブなユーザーでかつ年齢が18歳以上のユーザーだけを抽出するPredicate
を作成してください。
class User {
private String name;
private int age;
private boolean isActive;
// コンストラクタ、ゲッター
public boolean isActive() {
return isActive;
}
public int getAge() {
return age;
}
}
List<User> users = Arrays.asList(
new User("Alice", 20, true),
new User("Bob", 17, true),
new User("Charlie", 25, false),
new User("David", 30, true)
);
// TODO: Predicateを使用して、アクティブかつ年齢が18歳以上のユーザーのみを抽出
Predicate<User> isActiveAndAdult = user -> user.isActive() && user.getAge() >= 18;
List<User> eligibleUsers = users.stream()
.filter(isActiveAndAdult)
.collect(Collectors.toList());
eligibleUsers.forEach(user -> System.out.println(user.getName())); // 期待される出力: ["Alice", "David"]
解答の確認
これらの問題を解くことで、Predicate
を用いた条件付きフィルタリングの技術を実際に体験できます。コードが期待通りに動作したかどうかを確認し、もし異なる結果が得られた場合は、条件やロジックのどこに問題があるのかを見直してみてください。
さらなる挑戦
より高度な練習として、複数のPredicate
を組み合わせて、複雑な条件を作成してみましょう。また、自作のPredicate
を異なるシナリオで再利用してみることで、実際の開発における柔軟なコード設計を体感できます。
これらの演習を通じて、Predicate
を使ったフィルタリングや条件判定のスキルを習得し、実際の開発に活かせるようにしていきましょう。
まとめ
本記事では、JavaのPredicate
インターフェースとラムダ式を使った実装方法について解説しました。Predicate
は、条件判定を簡潔に表現できる強力なツールであり、データのフィルタリングや複雑な条件のチェーン処理など、さまざまな場面で活用できます。実際の開発シナリオや演習問題を通じて、Predicate
の基本から応用までの知識を深め、効率的で再利用可能なコードを作成するスキルを養いました。これにより、より安全で効率的なJavaプログラムを構築できるようになるでしょう。
コメント