Java 8 で導入された Stream の reduce()
の使い方と例を紹介します。
1. Stream.reduce()
Stream.reduce(accumulator)
関数は、Stream の要素を 1 つのデータにする作業を行います。
たとえば、Stream から 1 から 10 までの数値が渡されると、この値を合計して 55 の結果を返すことができます。 ここで演算を行う部分はアキュムレータ関数であり、直接実装して引数に渡す必要があります。
以下は、 reduce()
を使ってストリームから渡される要素の数をすべて合計する例です。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> sum = numbers.reduce((x, y) -> x + y);
sum.ifPresent(s -> System.out.println("sum: " + s));
Output:
sum: 55
上記の例を説明しながら reduce()
の動作方法について紹介します。
reduce()
は引数として BinaryOperator オブジェクトを受け取り、 BinaryOperator は T 型の引数を 2 つ受け取り、T 型のオブジェクトを返す関数型インタフェースです。
BinaryOperatorは (total, n) -> total + n
と同じ形式で引数が渡されます。 Stream の 1 が渡されるとき、total(0) + n(1) = 1
のように計算され、ここで返される 1 が次に Stream で 2 が渡されるとき total に渡されます。したがって、 total(1) + n(2) = 3
になります。
もう一度整理すると、次のように計算され、最後に1から10までの数字を加えた55が返されます。
- total(0) + n(1) = 1
- total(1) + n(2) = 3
- total(3) + n(3) = 6
- total(6) + n(4) = 10
- total(10) + n(5) = 15
- total(15) + n(6) = 21
- total(21) + n(7) = 28
- total(28) + n(8) = 36
- total(36) + n(9) = 45
- total(45) + n(10) = 55
1.1 メソッドリファレンスとして実装
(x, y) -> x + y
のように和を計算する関数は、JDK で Integer.sum(a, b)
という基本関数を提供します。
ここでメソッドリファレンスを使うと、 Integer::sum
のように、より短いコードで同じ結果を返すコードを実装できます。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> sum = numbers.reduce(Integer::sum);
sum.ifPresent(s -> System.out.println("sum: " + s));
2. 初期値のある Stream.reduce()
上記の例では、totalの初期値は0でした。
しかし、 Stream.reduce(init, accumulator)
のように初期値を引数として渡すことができます。
上記の例で初期値を10に設定すると、「10 + 1 + 2 + 3 ... 10」のように演算が行われます。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.reduce(10, (total, n) -> total + n);
System.out.println("sum: " + sum);
Output:
sum: 65
3. reduce() の並列処理
Stream.parallel()
は、Stream 演算を並列処理で行うようにします。つまり、 parallel()
とともに reduce()
を使用すると、順次演算を実行せずに複数の演算を同時に進行し、それらの作業を再びマージして最終的に 1 つの結果を生成します。
たとえば、 (1 + 2) + (3 + 4) + ... + (9 + 10)
のように2つずつ束ねて最初に計算し、結果を再計算できます。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.parallel().reduce(0, (total, n) -> total + n);
System.out.println("sum: " + sum);
Output:
sum: 55
ただし、減算演算の場合、並列処理は順次処理(並列ではない)と結果が異なります。
以下のコードを実行すると、-55ではなく-5が返されます。
結果が異なる理由は、 (1 - 2) - (3 - 4) - ... - (9 - 10)
のように演算が行われながら順次演算することと結果が変わるからです。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.parallel().reduce(0, (total, n) -> total - n);
System.out.println("sum: " + sum);
Output:
sum: -5
並列処理の過程で reduce()
を使うときは、上記のような問題がないことを確認する必要があります。
4. 並列処理で reduce() は順次処理
並列処理での操作順序によって発生する問題を解決するために、以下の例に示すように他の規則を追加できます。
上記の例と比較すると、 (total1, total2) -> total1 + total2
が追加され、並列に処理された結果の関係を示します。
もう一度説明すると、最初の演算と2番目の演算は合わなければならないというルールを追加したのです。このようにルールを追加すると、最初の演算の結果が次の演算に影響を与えるため、 reduce()
は操作を分割して処理できなくなります。
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.reduce(0,
(total, n) -> total - n,
(total1, total2) -> total1 + total2);
System.out.println("sum: " + sum);
Output:
sum: -55
Related Posts
- Java - AtomicReference使用方法
- Java - CountDownLatchを使用する方法、および例
- Java - ScheduledThreadPoolExecutor使用方法
- Java - SummaryStatisticsの使用方法(count、min、max、average)
- Java8 - 関数型インタフェース (Functional Interface) について
- Java - String配列をint配列に変換する
- Java - ArrayList要素の値を変更する方法、replaceAll()
- Java - 2つのリストが同じかどうかを比較
- Java - 配列から特定のIndex要素を削除する3つの方法
- Java - HashMapソート、4つの方法
- Java - 文字列を配列に変換する方法
- Java - ArrayListが空であることを確認する3つの方法
- Java - ArrayListの巡回、4つの方法
- Java - ArrayListの最大値、最小 値を見つける
- Java - ArrayListの合計、平均値の計算
- Java - HashMap巡回、3つの方法
- Java - do whileとwhileの違い
- Java - Lambda式と関数型インタフェース
- Java - List empty(null)チェック、3つの方法
- Java - ArrayListの初期化、4つの方法
- Java - Stream.reduce()の使い方と例
- Java - 2つのマップを結合する(merge、putAll)
- Java - java.util.Dateをjava.sql.Dateに変換する
- Java - ArrayListをStringに変換する
- Java - ClassNotFoundExceptionの発生原因と解決策
- Java - 非静的メソッドは静的コンテキストから参照できません
- Java - NoSuchMethodErrorの原因と解決策
- Java - JSONライブラリを使用する方法(JSONObject、JSONArray)
- Java - byte[]配列をFileに保存
- Java - byte[]の配列をStringに変換
- Java - ファイルのアクセス権を確認し、変更
- Java - 一時フォルダ(Temp directory)パスを取得する
- Javaでシェルスクリプトを実行
- Java - Streamを配列に変換する
- Java - リスト重複排除、2つの方法