Javaのラムダ式とStream API
Java8から導入された「ラムダ式」と「Stream API」の基本パターンです。1時間ぐらいでモダンな機能の基本をマスターできると思います。
目次
1. ラムダ式(LAMBDA)
2. Stream API(ラムダ式の活用)
2-1. List版
2-2. Set版
2-3. Map版
2-4. 配列をStreamに変換する
2-5. 並列処理
3. Stream APIの例
3-1. filter(フィルタリング/絞込み)
3-2. map(マッピング/値の編集)
3-3. allMatch,anyMatch,noneMatch(マッチング/真偽)
3-4. sorted(ソート/整列)
3-5. count(カウント/リスト数)
3-6. average(アベレージ/平均)
3-7. max(マックス/最大値)
3-8. min(ミニ/最小値)
3-9. limit(リミット/要素数の制限)
3-10. distinct(ディスティンクト/重複削除)
3-11. flatMap(フラットマップ/新リストの作成)
3-12. reduce(リダクション/要素を集計する)
3-13. collect(コレクト/オブジェクトを返す)
3-14. groupingBy(グルーピング/要素の分類)
3-15. まとめ
1. ラムダ式(LAMBDA)
ラムダ式では関数オブジェクトを即時利用(定義及び生成)できます。
// メソッドの型を作成する(インターフェース) interface MyFunction { int run(int x); } public class Main { public static void main(String[] args) {; // ラムダ式でメソッドの参照(ポインタ)を取得する MyFunction my = (int x) -> {return x * 10;}; // メソッドを実行する System.out.println(my.run(1)); // ラムダ式でメソッドを即時実行する int val = ((MyFunction)((int x) -> {return x * 10;})).run(1); System.out.println(val); } }
結果
10
コードの省略
ラムダ式はコードを次のように省略可能なのでスッキリします。
引数の型を省略できる。(型推論) |
引数が1つの場合は()を省略できる。 |
{}が単文の場合は{}とreturnを省略できる。 |
// メソッドの型を作成する(インターフェース) interface MyFunction { int run(int x); } public class Main { public static void main(String[] args) {; // 元のラムダ式 MyFunction my1 = (int x) -> {return x * 10;}; System.out.println(my1.run(1)); // 型の省略 MyFunction my2 = (x) -> {return x * 10;}; System.out.println(my2.run(1)); // ()の省略 MyFunction my3 = x -> {return x * 10;}; System.out.println(my3.run(1)); // {}とreturnの省略 MyFunction my4 = x -> x * 10; System.out.println(my4.run(1)); } }
結果
10
10
10
2. Stream API(ラムダ式の活用)
コレクションにStream APIとラムダ式を使用する事が可能です。
2-1. List版
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; // リストの生成 List<Integer> list = Arrays.asList(1,2,3); // 拡張for文 for(Integer x : list) { System.out.println(x); } System.out.println("---"); // Stream APIとラムダ式を活用する list.stream().forEach(x -> System.out.println(x)); System.out.println("---"); // 引数の型/個数が一致している場合は更に省略可能 list.stream().forEach(System.out::println); } }
結果
2
3
---
1
2
3
---
1
2
3
2-2. Set版
import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class Main { public static void main(String[] args) {; // リストの生成 Set<String> list = new HashSet<>(Arrays.asList("あ","い")); // 拡張for文 for(String x : list) { System.out.println(x); } System.out.println("---"); // Stream APIとラムダ式を活用する list.stream().forEach(x -> System.out.println(x)); System.out.println("---"); // 引数の型/個数が一致している場合は省略可能 list.stream().forEach(System.out::println); } }
結果
い
---
あ
い
---
あ
い
2-3. Map版
import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) {; Map<String,String> map = new HashMap<>(); map.put("R" ,"赤色"); map.put("G" ,"緑色"); map.put("B" ,"青色"); // 拡張for文 for(String key : map.keySet()) { System.out.println(key + ":" + map.get(key)); } System.out.println("---"); // Stream APIとラムダ式を活用する map.entrySet().stream() .map(v -> v.getKey() + ":" + v.getValue()) .forEach(x -> System.out.println(x)); System.out.println("---"); // 引数の型/個数が一致している場合は省略可能 map.entrySet().stream() .map(v -> v.getKey() + ":" + v.getValue()) .forEach(System.out::println); } }
結果 ※Mapは環境により順序が異なる
B:青色
G:緑色
---
R:赤色
B:青色
G:青色
---
R:赤色
B:青色
G:緑色
2-4. 配列をStreamに変換する
import java.util.Arrays; import java.util.stream.Stream; public class Main { public static void main(String[] args) {; Integer[] list = {1,2,3}; Stream<Integer> st = Arrays.stream(list); st.forEach(System.out::println); } }
結果
2
3
2-5. 並列処理
stream()の代わりにparallelStream()を使用すると並列処理を行います。環境によっては処理速度が速くなる場合があります。
3. Stream APIの例
次節からStreamの基本的なパターンをご紹介します。
公式APIの解説 | Stream |
---|---|
Comparator | |
Collectors |
不明点は公式のAPI解説書をご覧下さい。
3-1. filter(フィルタリング/絞込み)
SQLで言うとWHEREと同様です。
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3); // filter list.stream() .filter(i -> i <= 2) .forEach(System.out::println); } }
結果
2
3-2. map(マッピング/値の編集)
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<String> list = Arrays.asList("A","B","C"); // map list.stream() .map(s -> s.toLowerCase()) .forEach(System.out::println); } }
結果
b
c
3-3. allMatch,anyMatch,noneMatch(マッチング/真偽)
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; // リストの生成 List<String> list = Arrays.asList("A","B","A"); // allMatch(全て一致する) boolean flg1 = list.stream() .allMatch("A"::equals); System.out.println(flg1); // anyMatch(いずれかが一致する) boolean flg2 = list.stream() .anyMatch("A"::equals); System.out.println(flg2); // noneMatch(全て一致しない) boolean flg3 = list.stream() .noneMatch(a -> (a == "C")); System.out.println(flg3); } }
結果
true
true
3-4. sorted(ソート/整列)
SQLのORDER BYと同様。
import java.util.Arrays; import java.util.Comparator; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(3,1,2); // sorted(昇順) list.stream() .sorted(Comparator.naturalOrder()) .forEach(System.out::println); System.out.println("---"); // sorted(降順) list.stream() .sorted(Comparator.reverseOrder()) .forEach(System.out::println); } }
結果
2
3
---
3
2
1
3-5. count(カウント/リスト数)
SQLのCOUNTと同様。
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3); // count long val = list.stream().count(); System.out.println(val); } }
結果
3-6. average(アベレージ/平均)
SQLのAVGと同様。
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3); // average double val = list.stream() .mapToInt(x->x) .average() .getAsDouble(); System.out.println(val); } }
結果
3-7. max(マックス/最大値)
SQLのMAXと同様。
import java.util.Arrays; import java.util.Comparator; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3); // max Integer val = list.stream() .max(Comparator.naturalOrder()) .get(); System.out.println(val); } }
結果
3-8. min(ミニ/最小値)
SQLのMINと同様。
import java.util.Arrays; import java.util.Comparator; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3); // min Integer val = list.stream() .min(Comparator.naturalOrder()) .get(); System.out.println(val); } }
結果
3-9. limit(リミット/要素数の制限)
SQLのLIMITと同様。
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3,4,5); // limit list.stream() .limit(3) .forEach(System.out::println); } }
結果
2
3
3-10. distinct(ディスティンクト/重複削除)
SQLのDISTINCTと同様。
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,3,3,3,5); // distinct list.stream() .distinct() .forEach(System.out::println); } }
結果
3
5
3-11. flatMap(フラットマップ/新リストの作成)
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<String> list = Arrays.asList("1,2,3","4,5,6"); // flatMap list.stream() .flatMap(s -> Arrays.stream(s.split(","))) .forEach(System.out::println); } }
結果
2
3
4
5
6
3-12. reduce(リダクション/要素を集計する)
要素を集計して集計結果を返します。SQLのSUMに近いです。
<例1>
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3,4,5); // reduce Integer sum = list.stream() .reduce(Integer::sum) .get(); System.out.println(sum); } }
結果
<例2>
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3,4,5); // reduce Integer sum = list.stream() .reduce((a,b) -> a + b) .get(); System.out.println(sum); } }
結果
<例3>
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3,4,5); // reduce Integer sum = list.stream() .reduce(10,(a,b) -> a + b); System.out.println(sum); } }
結果
※stream()とparallelStream()では計算結果が異なるので注意して下さい。
3-13. collect(コレクト/オブジェクトを返す)
オブジェクトは次のストリームがある場合は「次のストリームへ」。ない場合は「戻り値」になります。※次節の3-14もご覧下さい。
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) {; List<Integer> list = Arrays.asList(1,2,3); // collect List<Integer> result = list.stream().collect(Collectors.toList()); result.forEach(System.out::println); } }
結果
2
3
3-14. groupingBy(グルーピング/要素の分類)
SQLのGROUP BYと同様。
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) {; List<String> list = Arrays.asList ("とら","たらんちゅあ","ねこ","しまうま","きりん"); // groupingBy list.stream() .collect(Collectors.groupingBy(s -> s.length())) .forEach((a,b) -> System.out.println(a + ":" + b)); } }
結果
3:[きりん]
4:[しまうま]
6:[たらんちゅあ]
3-15. まとめ
Stream APIはデータをSQLのように操作できる仕組みです。
また、mapやfilterなどはメソッドチェーン(メソッドを連続して呼び出す)で複数、組み合わせて使用する事が可能です。
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) {; List<String> list = Arrays.asList("とら","しまうま","きりん"); list.stream() .map(a->a + "A") .filter(a-> a.length() != 0) .filter(a-> a.length() < 5) .collect(Collectors.groupingBy(s -> s.length())) .forEach((a,b) -> System.out.println(a + ":" + b)); } }
結果
4:[きりんA]
初めての方には「やや難しい」ですが便利なので色々と試すと良いです。