lambdaについて調べてみた filter編 その1
【前提条件】
【概要】
今回はfilterについて調べてみました。
filterはStream内の要素に対して条件でフィルタするメソッドです。
フィルタでtrueが帰ってきた要素のみ後続の処理が行われます。
【単一のfilter】
まずはListから単一の条件でフィルタして、
コンソールに出力するサンプルからです。
[サンプルソース]
public static void main(final String[] args) { final List<String> list = new ArrayList<>(); list.add("one"); list.add("two"); list.add("three"); list.add("four"); System.out.println("======================== outputStringList ========================"); list.stream().filter(p -> 3 < p.length()).forEach(System.out::println); System.out.println("======================== outputStringList ========================"); }
filterメソッドはPredicateのオブジェクトをパラメータとして渡します。
Predicateインターフェイスはbooleanを返すtestメソッドが定義されています。
ラムダ式ではbooleanの結果を返すものを書くだけです。
[実行結果]
three four
文字列の長さが3を超える値のみが取得されているのがわかります。
【複数のFilter】
次にBeanの中身を複数の条件でフィル足して、
コンソールに出力してみます。
[Bean]
public class Personal { public final String name; public final int age; public final Arm arm; public Personal(final String name, final int age, final Arm arm) { this.name = name; this.age = age; this.arm = arm; } public boolean isSearchMathing() { return (40 < age && Arm.Right.equals(arm)); } public enum Arm { Right, Left } }
isSearchMathingメソッドはあとで
メソッド参照させるときに使うためのものです。
[サンプルソース]
public static void main(final String[] args) { final List<Personal> list = new ArrayList<>(); list.add(new Personal("シュンツ", 27, Personal.Arm.Right)); list.add(new Personal("コーツ", 38, Personal.Arm.Left)); list.add(new Personal("カンツ", 61, Personal.Arm.Right)); list.add(new Personal("ピンフ", 44, Personal.Arm.Left)); list.add(new Personal("トイトイ", 52, Personal.Arm.Left)); list.add(new Personal("チートイツ", 49, Personal.Arm.Right)); list.stream() .filter(p -> 40 < p.age) .filter(p -> Personal.Arm.Right.equals(p.arm)) .forEach(p -> System.out.println(p.name)); list.stream() .filter(p -> 40 < p.age && Personal.Arm.Right.equals(p.arm)) .forEach(p -> System.out.println(p.name)); list.stream() .filter(Personal::isSearchMathing) .forEach(p -> System.out.println(p.name)); }
複数の条件でフィルタをする場合にはfilterをメソッドチェインでつなげます。
もしくはfilterの中で複数の条件を書いたり、
Beanに条件用のメソッドを作ってメソッド参照で書いたりしてもできます。
【単一と複数の違い】
複数の条件でフィルタをする場合、
単一のfilterメソッドでやるのと複数のfilteメソッドチェインで行うのと
動きがどう変わるかを見てみます。
[サンプルソース]
public static void main(final String[] args) { // listの作成は先ほどのサンプルと同じなので省略 System.out.println("======================== outputPersonalList2 ========================"); final List<Personal> resultList = list.stream() .filter(p -> { final boolean returnValue = 40 < p.age; System.out.println(p.name + "is 40 over?" + returnValue); return returnValue; }) .filter(p -> { final boolean returnValue = Personal.Arm.Right.equals(p.arm); System.out.println(p.name + "is right arm?" + returnValue); return returnValue; }) .collect(Collectors.toList()); System.out.println("------------------------ result ------------------------"); resultList.forEach(p -> System.out.println(p.name)); System.out.println("------------------------ result ------------------------"); System.out.println("======================== outputPersonalList2 ========================"); }
各filterメソッドを呼び出している際に
コンソール出力させるようにしてみました。
[実行結果]
シュンツis 40 over?false コーツis 40 over?false カンツis 40 over?true カンツis right arm?true ピンフis 40 over?true ピンフis right arm?false トイトイis 40 over?true トイトイis right arm?false チートイツis 40 over?true チートイツis right arm?true ------------------------ result ------------------------ カンツ チートイツ ------------------------ result ------------------------
名前が「シュンツ」、「コーツ」と言うデータは
最初のfilter条件を満たしていないため、
二つ目のfilterメソッドが実行されていないことがわかります。
filter(A).filter(B).filter(C)というのがあった場合、
データごとにA -> B -> Cの順番で評価されるようです。
つまり、最初の条件で絞ってしまったほうが
後続の処理は少なくてすむということです。
【まとめ】
filterメソッドを使えば、
条件で後続の処理対象を絞れました。
filterメソッドをメソッドチェインで呼び出す場合は
定義した順番で評価されていきます。
データ件数が多い場合はfilterの仕方でパフォーマンスが
変わってきそうですね。