Stream メモ
投稿日: | |
---|---|
タグ: |
JavaのStreamに関するメモ。
Stream API
概要
Java8から導入されたStream APIは、簡単に言ってデータ集合を扱う機能である。 Stream APIを使うとループ文やイテレータでは、冗長な記載しかできなかった文が簡潔に書けることが多い。
ただし、プロジェクト内でうまくルールを決めないと、同じような実装をやるために、さまざまな書き方で書かれることになる。
また、性能的な観点でいると、簡単なデータ集合を扱うだけならば、わざわざStream型を使用するよりも、基本(プリミティブ)型を使ったほうが速い。
- ループ文
- java Hoge 0.30s user 0.02s system 99% cpu 0.318 total
String[] ary = {"hoge", "fuga", "piyo"}; for (int i=0;i<3;i++){ System.out.println(ary[i]); }
- Stream API
- java Hoge 0.46s user 0.06s system 113% cpu 0.460 total
String[] ary = {"hoge", "fuga", "piyo"}; Stream.of(ary).forEach(System.out::println);
基本
データ集合にはそれに対応するストリームの種類がある。
- オブジェクト型
- Stream
- 基本(プリミティブ)型
ストリーム 対応するプリミティブ型 IntStream int LongStream long DoubleStream double
ストリームは上記のStreamやIntStreamクラスなどのstaticメソッドや、配列やコレクション型などのデータ集合をストリームに型変換することで、記載する。
ストリームに0回以上の中間操作というメソッド呼び出しを行い、最後に終端操作と呼ばれるメソッドを呼ぶのがストリームの基本的な考え方である。
以下、0〜20の間の3の倍数の奇数を出力するサンプル。
IntStream.rangeClosed(0, 20) // ストリームを取得: 0〜20
.filter((v) -> v%3==0) // 中間操作: 3の倍数
.filter((v) -> v%2==1) // 中間操作: 奇数
.forEach(System.out::println); // 終端操作:各要素を引数にメソッド呼び出し
メソッドは以下のように記載する。
- クラス::メソッド
- オブジェクト::メソッド
レシピ集
- 要素に対する条件
- allMatch(条件)
- 全て一致
noneMatchというものもあるint [] ary = {1, 2, 3, 4, 5}; if (IntStream.of(ary).allMatch((i) -> i > 0)){ System.out.println("OK"); }
- noneMatch(条件)
int[] v = {0, 0, 0, 0}; if (IntStream.of(v).noneMatch((x) -> x > 0)){ System.out.println("OK"); }
- anyMatch(条件)
- いずれかが一致
int [] ary = {1, 2, 3, 4, 5}; if (IntStream.of(ary).anyMatch((i) -> i > 3)){ System.out.println("OK"); }
- 多次元配列から1次元配列へ
- flatMap(expression)のexpressionにはStreamを返すメソッドを指定する(flatMapToInt()の場合、IntStream)。
- オブジェクト型
String[][] ary = {{"a", "b"}, {"c", "d", "e"}}; String[] s = Stream.of(ary).flatMap(Stream::of) .toArray(String[]::new);
String[][][] ary = {{{"a", "b"}, {"c", "d", "e"}}, {{"f", "g"},{"h", "i"}}}; String[] s = Stream.of(ary).flatMap(Stream::of) .flatMap(Stream::of) .toArray(String[]::new);
- プリミティブ型
int[][] ary = {{123, 456}, {789, 101}}; int[] iary = Stream.of(ary).flatMapToInt(IntStream::of) .toArray();
int[][][][] ary = {{{{123, 456}, {789, 101}}, {{121, 131}, {415, 161}}}, {{{718, 192}, {212, 132}}}}; int[] iary = Stream.of(ary).flatMap(Stream::of) .flatMap(Stream::of) .flatMapToInt(IntStream::of) .toArray();
- int[]からList<Integer>への型変換
int [] ary = {1, 2, 3, 4, 5}; List<Integer> list = IntStream.of(ary).mapToObj(Integer::new) .collect(Collectors.toList());
- List<Integer>からint[]への型変換
int[] ary = list.stream().mapToInt((v) -> v).toArray();
- String[][]からint[]への型変換
String[][] ary = {{"123", "456"}, {"789", "101"}}; int[] iary = Stream.of(ary) .map((x) -> Stream.of(x).mapToInt((y) -> Integer.parseInt(y)).toArray()) .flatMapToInt((v) -> IntStream.of(v)) .toArray();
- int[]からint
int[] ary = {1, 2, 3, 4}; IntStream.of(ary).reduce((b, i) -> b + i).ifPresent(System.out::println);
- ListからMapへ
List.of("a", "b", "c").stream().collect(Function.identity(), e -> String.fomat("{%s}", e));
- List<Integer>の作成
連続する数字のストリーム。range()の範囲は「開始番号 ≦ n < 終了番号」 rangeClosed()の範囲は「開始番号 ≦ n≦ 終了番号」List<Integer> ary = IntStream.rangeClosed(1, 5) .mapToObj(Integer::new) .collect(Collectors.toList());
- Set<Integer>
List<Integer> ary = IntStream.rangeClosed(1, 5) .mapToObj(Integer::new) .collect(Collectors.toSet());
- ArrayList<String>[]
@SuppressWarnings("unchecked") public static void main(String[] args){ ArrayList<String>[] ary = Stream.generate(ArrayList::new).limit(5).toArray(ArrayList[]::new); }
- 数列
- 乱数配列
generate()は無限に続くストリーム。遅延評価されるため、実際に使用する際は基本的にlimit(n)のように使用する分だけを指定する。double[] ary = DoubleStream.generate(() -> Math.random()).limit(3).toArray();
- 0.39132915962622816
- 0.7227708950486765
- 0.47467761699532696
- 配列に変換(1〜4)
int[] a = IntStream.range(1, 5).toArray()
- 終了値を含める場合(1〜5)
int[] a = IntStream.rangeClosed(1, 5).toArray()
- 1, 4, 9, 16, 25,..
IntStream.rangeClosed(1, 5).map((x) -> x * x);
- 2, 4, 6, 8, 10,..
iterate()はlimit()がないと無制限に続くStream.iterate(2, x -> x + 2).limit(8)
Stream.iterate(初期値, 増加式)
文字列結合
Java8からString.join()やStringJoinerというクラスが導入された。 これらを使うと文字列結合が非常に便利になる。
また、Streamと組み合わせると更に記載がシンプルになる。
- hoge,fuga,piyo,foo
String.join(",", ary);
- レコード:{hoge,fuga,piyo,foo}
Stream.of(ary).collect(Collectors.joining(",", "レコード:{", "}"));
Appendix
ストリーム取得
- Arrays.stream(配列)
- Stream.of(配列)
- Stream.of(要素, ...)
- コレクション型.stream()
中間操作
- Stream.filter(expression)
- 条件に合致するもののみを抽出する
- Stream.distinct()
- Streamから要素の重複を排除したものを取得する。
- Stream.sorted()
- Stream.sorted(expression)
- ソートしたストリームを取得する
- Stream.map(expression)
- Stream.mapToInt(expression)
- Stream.mapToLong(expression)
- Stream.mapToDouble(expression)
- IntStream.mapToObj(expression)
- LongStream.mapToObj(expression)
- DoubleStream.mapToObj(expression)
- 各要素を別の型に変換する。map()やmapToObj()はStream型を返し、それ以外は対応するストリームを返す(例:mapToInt()であればIntStream)。
- Stream.flatMap(expression)
- Stream.flatMapToInt(expression)
- Stream.flatMapToLong(expression)
- Stream.flatMapToDouble(expression)
- 各要素をストリームとして、それらを結合したものを取得する expressionには対応するストリームを返すメソッドを指定する。
- Stream.parallel()
- IntStream.parallel()
- LongStream.parallel()
- DoubleStream.parallel()
- 並列実行用のストリームを取得する
終端操作
- forEachOrdered(expression)
- forEach(expression)
- メソッド呼び出し。返り値なし。
- Stream.count()
- Streamの要素数をカウント。返り値は要素数。
- IntStream.sum()
- LongStream.sum()
- Streamの要素を合計する。返り値は和。