Java8言語仕様でよく使う書き方のまとめ【Stream、Lambda関連】
背景
最近良く使っているJava8の構文についてまとめておきます。普段似たような処理を書きたいときに「あれっ?どんな感じで書くんだっけ?(;´Д`)」となるので備忘録的なメモです。
小言
以前はif/for文ぐらいを駆使してゴリゴリ書いていたので言語仕様に振られない範囲で書けましたが、Streamが出てきて言語仕様レベルから表現が豊かになった(=メソッドの選択肢と組合せが豊富)ので、書くとき結構悩みます(;´Д`)
前提
以下のクラスを利用しています。- 特記事項
@Setter @Getter public class Person{ private String name; }
メモ
既存のコレクションについて、型を変換したコレクションが欲しい
例:名前(文字列)一覧から、対応する人インスタンス(Person型)に変換する- Java8仕様
- sequential()は、コレクションの実行順序を保証したStreamを作る。必要とは限らないが今回はテスト結果確認のため付けています。
- map()は、コレクションの各要素が渡ってきて(n:変数名は任意)、その値を使って処理して任意の型を返していいメソッド。
- collect(Collectors.toList())は、結果のListを作るための定型文。
@Test public void test_lambda_list(){ // setup final List<String> names = Arrays.asList("namihira", "john", "james"); // action final List<Person> persons = names.stream() .sequential() .map(n -> { final Person p = new Person(); p.setName(n); return p; }) .collect(Collectors.toList()); // check assertEquals(names.size(), persons.size()); assertEquals(names.get(0), persons.get(0).getName()); assertEquals(names.get(1), persons.get(1).getName()); assertEquals(names.get(2), persons.get(2).getName()); }
- ちなみにJava8以前の仕様で書くと
@Test public void test_no_lambda_list(){ // setup final List<String> names = Arrays.asList("namihira", "john", "james"); //action final List<Person> persons = new ArrayList<>(); for (String name : names) { final Person p = new Person(); p.setName(name); persons.add(p); } //check assertEquals(names.get(0), persons.get(0).getName()); assertEquals(names.get(1), persons.get(1).getName()); assertEquals(names.get(2), persons.get(2).getName()); }
既存のコレクションについて、指定のものだけについて型を変換したコレクションが欲しい
例:名前(文字列)一覧から、イニシャルが"j"の人だけインスタンス(Person型)一覧を取得する。- Java8仕様
- filter()は、中にtrue/falseを返す式を書いてtrueになったものだけを後工程に渡す。今回は「イニシャルが"j"であるか」がその条件式。
@Test public void test_lambda_list_filter(){ // setup final List<String> names = Arrays.asList("namihira", "john", "james"); // action final List<Person> persons = names.stream() .sequential() .filter(n -> n.startsWith("j")) .map(n -> { final Person p = new Person(); p.setName(n); return p; }) .collect(Collectors.toList()); // check assertEquals(names.size() - 1, persons.size()); assertEquals(names.get(1), persons.get(0).getName()); assertEquals(names.get(2), persons.get(1).getName()); }
- ちなみにJava8以前の仕様で書くと
@Test public void test_no_lambda_list_filter(){ // setup final List<String> names = Arrays.asList("namihira", "john", "james"); //action final List<Person> persons = new ArrayList<>(); for (String name : names) { if (name.startsWith("j")) { final Person p = new Person(); p.setName(name); persons.add(p); } } //check assertEquals(names.size() - 1, persons.size()); assertEquals(names.get(1), persons.get(0).getName()); assertEquals(names.get(2), persons.get(1).getName()); }
既存のコレクションについて型を変換したコレクションが欲しいけど、ソートもしておきたい
例:名前(文字列)一覧からインスタンス(Person型)一覧への変換と名前順にする。- Java8仕様
- sorted()は、中にソート条件(Comparator)を書いてそのソートした結果を後工程に渡す。
- String::compareToはメソッド参照の書き方。
- 一番ゴリゴリに書くなら、Comparatorクラスをnewして書く。後述の「Java8以前の記載」参照。
- ↑しかし、Comparatorクラスはメソッドが1つしかない(compare)ので*1、コンパイラが空気を読んで短縮して記載できる(=Lambda式)。
- ↑ComparatorクラスをnewをLambda式を利用して書くと「sorted( (n1, n2) -> n1.compareTo(n2) )」のように書ける。
- ↑さらに今回のように「渡ってきた文字列を比較する」自体はコンパイラ的には自明なようなので短縮して記載できる(=メソッド参照)。「StringクラスのcompareToメソッドでいつものアレでお願い」というイメージ*2(´Д`)。
- で結局以下の「String::compareTo」のような記載になる。
- String::compareToはメソッド参照の書き方。
- sorted()は、中にソート条件(Comparator)を書いてそのソートした結果を後工程に渡す。
@Test public void test_lambda_list_sort(){ // setup final List<String> names = Arrays.asList("namihira", "john", "james"); // action final List<Person> persons = names.stream() .sorted(String::compareTo) .sequential() .map(n -> { final Person p = new Person(); p.setName(n); return p; }) .collect(Collectors.toList()); // check assertEquals(names.size(), persons.size()); assertEquals("james", persons.get(0).getName()); assertEquals("john", persons.get(1).getName()); assertEquals("namihira", persons.get(2).getName()); }
- ちなみにJava8以前の仕様で書くと
- Lambda式に慣れてくると、「new Comparator
() { @Override・・・」あたりがすごく冗長に見えてくる(;´Д`)
- Lambda式に慣れてくると、「new Comparator
@Test public void test_no_lambda_list_sort(){ // setup final List<String> names = Arrays.asList("namihira", "john", "james"); // action Collections.sort(names, new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.compareTo(s2); } }); final List<Person> persons = new ArrayList<>(); for (String name : names) { final Person p = new Person(); p.setName(name); persons.add(p); } // check assertEquals(names.size(), persons.size()); assertEquals("james", persons.get(0).getName()); assertEquals("john", persons.get(1).getName()); assertEquals("namihira", persons.get(2).getName()); }
既存のコレクションについて、各要素のある値だけをかき集めたい
例:人(Person型)一覧から名前だけを抽出したい。- Java8仕様
@Test public void test_lambda_list_method_ref(){ // setup final Set<String> names = new HashSet<>(Arrays.asList("namihira", "john", "james")); final List<Person> persons = names.stream() .sequential() .map(n -> { final Person p = new Person(); p.setName(n); return p; }) .collect(Collectors.toList()); // action final Set<String> result = persons.stream() .map(Person::getName) .collect(Collectors.toSet()); // check assertEquals(names, result); }
- ちなみにJava8以前の仕様で書くと
@Test public void test_no_lambda_list_method_ref(){ // setup final Set<String> names = new HashSet<>(Arrays.asList("namihira", "john", "james")); final List<Person> persons = names.stream() .sequential() .map(n -> { final Person p = new Person(); p.setName(n); return p; }) .collect(Collectors.toList()); // action final Set<String> result = new HashSet<>(); for (Person p : persons) { result.add(p.getName()); } // check assertEquals(names, result); }
既存のコレクションについて、グループ分けしたい
例:名前一覧について、イニシャルでグルーピングしたい。- Java8仕様
- collect×CollectorsはStream関連でも結構バラエティに富んだ表現があるが、一番使うのがこのパターン。
- 詳細はJavadoc参照(;´Д`)※基本どういう結果になるのかが読み解けないので、トライ・アンド・エラーで試してみるのが一番だったりする。
@Test public void test_lambda_list_grouping(){ // setup final List<String> names = Arrays.asList("namihira", "john", "james"); // action final Map<String, List<String>> result = names.stream() .collect(Collectors.groupingBy( n -> String.valueOf(n.charAt(0)), Collectors.toList() ) ); // check assertEquals(2, result.keySet().size()); assertEquals(1, result.get("n").size()); assertEquals(2, result.get("j").size()); assertTrue(result.get("n").contains("namihira")); assertTrue(result.get("j").contains("john")); assertTrue(result.get("j").contains("james")); }
- ちなみにJava8以前の仕様で書くと
@Test public void test_no_lambda_list_grouping(){ // setup final List<String> names = Arrays.asList("namihira", "john", "james"); // action final Map<String, List<String>> result = new HashMap<>(); for (String name : names) { final String initial = String.valueOf(name.charAt(0)); List<String> list = result.get(initial); if (list == null) { list = new ArrayList<>(); } list.add(name); result.put(initial, list); } // check assertEquals(2, result.keySet().size()); assertEquals(1, result.get("n").size()); assertEquals(2, result.get("j").size()); assertTrue(result.get("n").contains("namihira")); assertTrue(result.get("j").contains("john")); assertTrue(result.get("j").contains("james")); }
まとめ
毎回書いて、毎回忘れる(;´Д`)参考
Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング
- 作者: Cay S. Horstmann
- 出版社/メーカー: インプレス
- 発売日: 2015/04/03
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
- www.slideshare.net