要素の検索
今では、コレクションを変換するこのエレガントに設計された方法に慣れ親しんでいますが、要素の検索には役に立ちません。しかし、このためにフィルターメソッドが生まれました。
ここで、名前のリストから N で始まる名前を削除したいと思います。もちろん、何も存在せず、結果が空のセットになる場合もあります。まずは古い方法で実装してみましょう。
次のようにコードをコピーします。
Final List<String> startsWithN = new ArrayList<String>();
for(文字列名:友達) {
if(name.startsWith("N")) {
startWithN.add(名前);
}
}
このような単純なイベントに対して多くのコードを記述するのは非常に冗長です。まず変数を作成し、それを空のコレクションに初期化します。次に、元のコレクションを反復処理して、指定した文字で始まる名前を見つけます。見つかった場合は、コレクションに挿入されます。
フィルター メソッドを使用して上記のコードを再構築し、それがどれほど強力であるかを確認してみましょう。
次のようにコードをコピーします。
最終 List<String> で始まる N =
friends.stream()
.filter(名前 -> 名前.startsWith("N"))
.collect(Collectors.toList());
フィルター メソッドは、ブール値を返すラムダ式を受け取ります。式が true と評価された場合、実行コンテキスト内のその要素は結果セットに追加され、そうでない場合はスキップされます。最終的に返されるのは Steam で、これには式が true を返す要素のみが含まれます。最後に、collect メソッドを使用してコレクションをリストに変換します。この方法については、52 ページの「collect メソッドと Collecters クラスの使用」で詳しく説明します。
この結果セット内の要素を出力してみましょう。
次のようにコードをコピーします。
System.out.println(String.format("%d 個の名前が見つかりました",startsWithN.size()));
出力から、このメソッドがコレクション内の一致する要素をすべて見つけたことは明らかです。
次のようにコードをコピーします。
2人の名前が見つかりました
filter メソッドも、map メソッドと同様にイテレータを返しますが、それだけです。マップによって返されるコレクションは入力コレクションと同じサイズですが、フィルターがどのようなものを返すかを言うのは困難です。返されるセットのサイズ範囲は、0 から入力セット内の要素の数までです。マップとは異なり、フィルターは入力セットのサブセットを返します。
これまでのところ、ラムダ式によってもたらされるコードの簡素化には非常に満足していますが、注意しないとコードの冗長性の問題が徐々に大きくなり始めます。この問題については以下で説明しましょう。
ラムダ式の再利用
ラムダ式は非常に簡潔に見えますが、実際には、注意しないとコードが冗長になりやすくなります。冗長性があるとコードの品質が低下し、メンテナンスが困難になります。変更を加える場合は、関連する複数のコードをまとめて変更する必要があります。
冗長性を回避することもパフォーマンスの向上に役立ちます。関連するコードが 1 か所に集中しているため、そのパフォーマンスを分析し、ここでコードを最適化することができます。これにより、コードのパフォーマンスを簡単に向上させることができます。
ここで、ラムダ式の使用がコードの冗長性につながりやすい理由を見て、それを回避する方法を考えてみましょう。
次のようにコードをコピーします。
最終リスト<String> の友達 =
Arrays.asList("ブライアン", "ネイト", "ニール", "ラジュ", "サラ", "スコット");
最終 List<String> エディタ =
Arrays.asList("ブライアン", "ジャッキー", "ジョン", "マイク");
最終 List<String> 仲間 =
Arrays.asList("ケイト", "ケン", "ニック", "ポーラ", "ザック");
特定の文字で始まる名前を除外したいと考えています。
特定の文字で始まる名前をフィルタリングしたいと考えています。まずはフィルターメソッドを使って簡単に実装してみましょう。
次のようにコードをコピーします。
最終的な長いカウントFriendsStartN =
friends.stream()
.filter(名前 -> 名前.startsWith("N")).count();
最終的な長いカウントEditorsStartN =
editors.stream()
.filter(名前 -> 名前.startsWith("N")).count();
最終的な長いカウントComradesStartN =
同志.stream()
.filter(名前 -> 名前.startsWith("N")).count();
ラムダ式はコードを簡潔に見せますが、知らず知らずのうちにコードに冗長性をもたらします。上の例では、ラムダ式を変更したい場合、複数の場所を変更する必要がありますが、これは不可能です。幸いなことに、ラムダ式を変数に割り当て、オブジェクトと同じように再利用できます。
ラムダ式の受信者であるフィルター メソッドは、java.util.function.Predicate 関数インターフェイスへの参照を受け取ります。ここでも Java コンパイラが役に立ちます。指定されたラムダ式を使用して Predicate のテスト メソッドの実装を生成します。パラメータが定義されている場所でメソッドを生成するのではなく、Java コンパイラにこのメソッドを生成するようより明示的に要求できるようになりました。上記の例では、ラムダ式を型 Predicate の参照に明示的に格納し、この参照をフィルター メソッドに渡すことで、コードの冗長性を簡単に回避できます。
前のコードをリファクタリングして、DRY 原則に準拠するようにしましょう。 (「同じことを繰り返さない - DRY - の原則については、『The Pragmatic Programmer: From Journeyman to Master [HT00]』という本を参照してください)。
次のようにコードをコピーします。
Final Predicate<String> startsWithN = name -> name.startsWith("N");
最終的な長いカウントFriendsStartN =
friends.stream()
.filter(N で始まる)
。カウント();
最終的な長いカウントEditorsStartN =
editors.stream()
.filter(N で始まる)
。カウント();
最終的な長いカウントComradesStartN =
同志.stream()
.filter(N で始まる)
。カウント();
ここでは、ラムダ式を再度記述する代わりに、ラムダ式を一度記述し、startsWithN と呼ばれるタイプ Predicate の参照に格納します。次の 3 つのフィルター呼び出しでは、Java コンパイラーは述語として偽装されたラムダ式を認識し、微笑んでそれを黙って受け入れました。
この新しく導入された変数により、コードの冗長性が排除されます。しかし、残念なことに、後でわかるように、敵はすぐに復讐のために戻ってきます。さらに強力な武器が私たちのために彼らを破壊できるか見てみましょう。