Javaを学ぶ -Java 8 Stream編-
背景
Streamとは
Java SE 8から実装された、コレクション(配列、リスト等)を扱いやすくするAPI。
参照: Stream (Java Platform SE 8)
それまで拡張for文等で煩雑になりがちだった、コレクションに対する操作を明快にしてくれる。
Streamでは同じくJava SE 8で実装された、これまた重要なラムダ式を用いている。
...が、今回はラムダ式に関する解説は省略する。後日また改めて記事でまとめる予定。
Streamの構造
Streamは以下のような構造を持つ。
collection.stream() // streamの取得 .filter() // 中間操作 .sum(); // 終端操作
上記の例で解説していく。
stream()
...streamの取得
Collection.stream()
メソッドによって、コレクションのストリームを作成する。 ここで作成したストリームに対して操作を行っていくことになる。filter()
...中間操作
取得したストリームに対して、0個以上の中間操作を行う。
代表的なものはfilter(Predicate)
で、あるストリームを別のストリームに変換する。sum()
...終端操作
0個以上の中間操作を行った後、1つの終端操作を行う。
代表的なものにcount()
やsum()
,forEach(Consumer)
等がある。
Streamのサンプル
サンプル1: リスト内の奇数を合計し、出力する
- Streamを使わない場合
List<Integer> integerList = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = 0; for(Integer i : integerList) { if (i % 2 != 0) { sum += i; } } System.out.println(sum); // 25
- Streamを使う場合
List<Integer> integerList = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = integerList.stream() .mapToInt(x -> x) .filter(i -> i % 2 != 0) .sum(); System.out.println(sum); // 25
Streamを用いた記述の方が圧倒的にシンプルな上に読みやすい。
サンプル2: 各要素を操作し、新しいコレクションを生成する
- Streamを使わない場合
List<String> langs = Arrays.asList("Java", "Ruby", "Go", "Python"); List<String> langWithBlacket = new ArrayList<>(); for(String lang : langs) { langWithBlacket.add("[" + lang + "]"); } System.out.println(langWithBlacket); // [[Java], [Ruby], [Go], [Python]]
- Streamを使う場合
List<String> langs = Arrays.asList("Java", "Ruby", "Go", "Python"); System.out.println(langs.stream() .map(s -> "[" + s + "]") .collect(Collectors.toList())); // [[Java], [Ruby], [Go], [Python]]
場合によっては新規の変数を準備する必要がなく、記述量が減る。
サンプル3: コレクション内の要素全てに判定をかける
- コレクション内の要素に空白が含まれていないか判定
List<String> names = Arrays.asList("John", "Mark", "Andy", "Judy"); System.out.println(names.stream() .allMatch(s -> !s.isEmpty())); // true
- コレクション内の要素に条件を満たすものがあるか判定
List<String> names = Arrays.asList("John", "Mark", "Andy", "Judy"); System.out.println(names.stream() .anyMatch(s -> s.startsWith("A"))); // true List<String> names = Arrays.asList("John", "Mark", "Andy", "Judy"); System.out.println(names.stream() .anyMatch(s -> s.startsWith("K"))); // false
個人的にこの機能はものすごく便利。
他にも、ラムダ式の強みを活かしてインタフェースを扱うことも可能。
まとめ
煩雑になりがちだったコレクションに対する操作を、シンプルに記述出来るStreamは非常に便利で、扱う機会が多いため学習した。
特に、少ない記述量で中間操作をチェーン出来る点が非常に良いと感じた。
だいぶ前にリリースされたJava SE 8だが、ラムダ式とともにまだまだ学習が足りない為、継続的に学習していく。