本連載では、今までJavaの経験はあっても「ラムダ式は、まだ知らない」という人を対象にラムダ式について解説していきます。今回は、Java 8の新機能Stream APIの使い方について。Streamの生成、中間的な処理を行うメソッド、最終的な処理を行うメソッド、結果として使われるOptionalについてコード例を交えて解説します。
前回の「ラムダ式で本領を発揮する関数型インターフェースとStream APIの基礎知識」では、ラムダ式を使うことで本領を発揮する汎用的な関数型インターフェースとStream APIの概要を見てきました。
今回は、前回に引き続きStream APIについて見ていき、主なメソッドとその使い方を学んでいきましょう。
Stream APIを使うには、Streamのインスタンスを生成しなければいけません。まずは、そのStreamインスタンスを生成する方法を見ていきましょう。
Streamは配列やCollectionなどから作成可能です。まずは配列やCollectionからStreamのインスタンスを生成する主なメソッドを見てみましょう。下記のものが主なメソッドです。
呼び出し元クラス/インターフェース | メソッド | 概要 |
---|---|---|
Collection<T> | stream() | Collectionの要素を元にStreamのインスタンスを生成 |
Arrays | stream(T[] array) | 引数のarrayの要素を元にStreamのインスタンスを生成 |
Stream | of(T… values) | 引数のvaluesを元にStreamのインスタンスを生成。また引数に配列を渡した場合、その配列の要素でできたStreamインスタンスを生成することも可能 |
List<String> list = Arrays.asList( "list1", "list2", "list3", "list4", "list5" ); String[] array = {"配列1", "配列2", "配列3", "配列4", "配列5"}; // Streamインスタンスの生成 Stream<String> stream1 = list.stream(); Stream<String> stream2 = Arrays.stream(array); Stream<String> stream3 = Stream.of("1", "2", "3", "4", "5"); Stream<String> stream4 = Stream.of(array); // Streamの各要素を標準出力します。 stream1.forEach(e -> System.out.println("stream1:" + e)); stream2.forEach(e -> System.out.println("stream2:" + e)); stream3.forEach(e -> System.out.println("stream3:" + e)); stream4.forEach(e -> System.out.println("stream4:" + e));
stream1:list1 stream1:list2 stream1:list3 stream1:list4 stream1:list5 stream2:配列1 stream2:配列2 stream2:配列3 stream2:配列4 stream2:配列5 stream3:1 stream3:2 stream3:3 stream3:4 stream3:5 stream4:配列1 stream4:配列2 stream4:配列3 stream4:配列4 stream4:配列5
ここで注意すべき点は、数値を扱うStreamを生成する際に、指定する型が数値のプリミティブ型なのかラッパークラスなのかによって、返ってくるStreamが変わることです。
例えばArrays#streamメソッドの場合、引数の配列が数値のプリミティブ型(intなど)だと、それに対応した型のStream(IntStreamなど)を返します。しかし、引数がプリミティブ型のラッパークラス(Integerなど)だとStream<ラッパークラス>を返します。
int[] intArray = {1, 2, 3, 4, 5}; Integer[] integerArray = {1, 2, 3, 4, 5}; IntStream intArrayStream = Arrays.stream(intArray); Stream<Integer> integerArrayStream = Arrays.stream(integerArray);
次に、2つのStreamの要素を合わせて1つのStreamを生成するメソッドを見てみましょう。
呼び出し元クラス/インターフェース | メソッド | 概要 |
---|---|---|
Stream<T> | concat(Stream<? extends T> s1, Stream<? extends T> s2) | 要素が同じ型、もしくは、親が同じ型を持つs1とs2のStreamを結合し1つのStreamのインスタンスを生成 |
Stream<String> stream1 = Stream.of("あ", "い", "う", "え", "お"); Stream<String> stream2 = Stream.of("ア", "イ", "ウ", "エ", "オ"); Stream<String> concatStream = Stream.concat(stream1, stream2); concatStream.forEach(value -> System.out.println(value));
あ い う え お ア イ ウ エ オ
また、このconcatメソッドの引数が1つでも並列処理のStreamの場合、戻り値のStreamも並列処理を行うものになります。
Stream<String> stream1 = Stream.of("あ", "い", "う", "え", "お"); Stream<String> stream2 = Stream.of("ア", "イ", "ウ", "エ", "オ"); Stream<String> parallelStream = stream1.parallel(); Stream<String> concatStream = Stream.concat(parallelStream, stream2); concatStream.forEach(value -> System.out.println(value)); System.out.println("concatStream.isParallel()=" + concatStream.isParallel());
イ ア エ い あ お え ウ う オ concatStream.isParallel()=true
前回の連載で説明したようにJava 8では並列処理を行うStreamインスタンスを簡単に生成するためのメソッドが用意されています。並列処理を行うStreamを生成する場合は下記のメソッドが使えます。
呼び出し元クラス/インターフェース | メソッド | 概要 |
---|---|---|
Collection | parallelStream() | Collectionを元に並列処理を行うStreamのインスタンスを生成 |
BaseStream | parallel() | 呼び出し元のStreamから並列処理を行うStreamを返す |
また、Streamのインスタンスが並列処理を行うものかどうかチェックするにはBaseStream#isParallelメソッドで確認可能です。このメソッドを実行した際にTrueが返れば、並列処理を行うものになります。
逆に、並列処理から直列処理のStreamを生成する場合はBaseStream# sequentialメソッドで直列処理のStreamを生成可能です。
List<String> list = Arrays.asList( "list1", "list2", "list3", "list4", "list5" ); // 並列処理を行うStreamを生成 Stream<String> parallelStream1 = list.parallelStream(); Stream<String> parallelStream2 = list.stream().parallel(); // 生成されたStreamが並列処理を行うものか確認 System.out.println("parallelStream1.isParallel()=" + parallelStream1.isParallel()); System.out.println("parallelStream2.isParallel()=" + parallelStream2.isParallel()); // 並列処理から直接処理に変換 Stream<String> sequentialStream = parallelStream1.sequential(); System.out.println("sequentialStream.isParallel()=" + sequentialStream.isParallel());
parallelStream1.isParallel()=true parallelStream2.isParallel()=true sequentialStream.isParallel()=false
Copyright © ITmedia, Inc. All Rights Reserved.