当サイトは、アフィリエイト広告を利用しています
javaのstreamAPIのreduceを使いながら整理してみる。
下記の本では streamAPI についても詳しく書いてありおススメです!
また当ブログで紹介しているjava8のstreamAPIを使った
コレクションや配列の操作方法を
下記記事でメソッド別にまとめています!
java8のreduceにはパラメータが1個、2個、3個のversionと三つある
まずはパラメータが一つのメソッド。
戻り値がオプショナル型となるため注意が必要。
戻り値をオプショナル型で帰ってくるため、後処理が必要。
パラメータとしては二つの引数を与えて、一つの引数と同じ型の結果を返す関数型インターフェースの
BinaryOperator型の関数を渡す。
BinaryOperatorは引数、戻り値の型を一つしか設定できない。
Optional<T> reduce(BinaryOperator<T> accumulator);
public static void main(final String[] args) {final List<String> list = new ArrayList<>();list.add("one");list.add("two");list.add("three");final Optional<String> result = list.stream().reduce(// BinaryOperator型の関数を定義(accum, value) -> {return accum + "-" + value;});//結果はoptionalSystem.out.println(result.orElse(""));}// 実行結果// one-two-three
BinaryOperatorのオブジェクト(関数)がパラメータになる
内部では途中結果を保持するためのアキュムレータと
要素の値をパラメータとして受け取る、BinaryOperatorのオブジェクト(関数)が生成される。
reduceのパラメータに関数を渡して処理させるイメージ。(高階関数)
最初にreduceが実行される段階ではaccumが"one"、 valueが"two"で実行され
2回目はaccumが"one-two"、valueが"three"で実行される。
次にパラメータが2つのメソッド。
初期値を設定できるため一番使う気がする。
第一引数(identity)で初期値を設定するため、戻り値は
初期値の型となる。
第二引数にはパラメータが一つの時と同様にBinaryOperator型の関数を設定する。
T reduce(T identity, BinaryOperator<T> accumulator);
public static void main(final String[] args) {final List<String> list = new ArrayList<>();list.add("one");list.add("two");list.add("three");// // BinaryOperator型の関数を定義final BinaryOperator<String> operation =(accum, value) -> {return accum + "-" + value;};// 中身のあるListに対して実行final String result1 = list.stream().reduce("value", operation);// 空Listに対して実行final String result2 = new ArrayList<String>().stream().reduce("value", operation);System.out.println(result1);System.out.println(result2);}// 実行結果// value-one-two-three// value
パラメータが2つの場合はStream内要素と同じ型のオブジェクトと
BinaryOperatorのオブジェクトがパラメータなります
パラメータが2つの場合は1つめのパラメータに渡した値が
アキュムレータの初期値になる
※パラメータが1つの場合は自動的に最初の要素がアキュムレータの初期値となっていた
2つめのパラメータは実際に処理を行うBinaryOperator型の関数を設定する
戻り値の方はStream内のオブジェクトと同じ型が返却される
第一引数(初期値)に明示的にnullを渡した場合はヌルぽが発生する。
StreamAPIについて調べてみた reduce編 その1
パラメータ3つのメソッドの場合は、戻り値に要素と違う型を返すことができる。
※上記二つは用途と同じ型しか返せない
第一引数(identity)は初期値。
第二引数には二つの引数で指定した型で戻り値を返すBiFunction型の関数を設定する。
第三引数にはBinaryOperator型の関数の設定する。
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);
文字列のリストからリスト内のlenthを合計した値を返す。
引数が3つ以外では文字列結合した値しか返せない
import java.util.*;import java.math.BigDecimal;public class Main {public static void main(String[] args) throws Exception {List<String> fruits = Arrays.asList("apple", "orange", "kiwi");int result = fruits.stream().reduce(0, // 初期値(sum, elm) -> sum + elm.length(), // accumulator. 中間生成物を作る.(sum1, sum2) -> sum1 + sum2); // combiner. 中間生成物どうしをマージする.// 結果を出力System.out.println(result);}}// 結果// 15
下記では実際に使ってみたサンプルをいくつか乗せる。
オブジェクトのBigDecimal型のプロパティをreduceで合計する場合
第二引数はメソッド参照を使うことができる。
またオブジェクトリストに対してreduceを使う場合はmapで
算出対象プロパティだけを抽出する。
import java.util.*;import java.math.BigDecimal;public class Main {public static void main(String[] args) throws Exception {// 、class Person {private final String name;private final BigDecimal weight;Person(String name, BigDecimal weight) {this.name = name;this.weight = weight;}public BigDecimal getWeight() {return this.weight;}}Person p1 = new Person("太郎", new BigDecimal("72.1"));Person p2 = new Person("二郎", new BigDecimal("79.1"));List<Person> personList = Arrays.asList(p1, p2);BigDecimal totalWeight = personList.stream()//メソッド参照でWeightだけ取得.map(Person::getWeight)// Weightだけrecudeで集計.reduce(BigDecimal.ZERO, BigDecimal::add);System.out.println(totalWeight);}}
BigDecimal型の使い方については下記の記事で紹介しています。
streamを使ってBigDecimalの計算をする - sum編 -
集計を出したいプロパティだけリストをmapで取り出してからrecudeする
reduceの第二引数には関数を設定する
import java.util.*;public class Main {public static void main(String[] args) throws Exception {class Person {private final String name;private final int weight;Person(String name, int weight) {this.name = name;this.weight = weight;}public int getWeight() {return this.weight;}}Person p1 = new Person("太郎", 72);Person p2 = new Person("二郎", 79);List<Person> personList = Arrays.asList(p1, p2);int totalWeight = personList.stream()//メソッド参照でWeightだけ取得.map(Person::getWeight)// Weightだけrecudeで集計.reduce(0, (accum,value)->accum + value);System.out.println(totalWeight);}}
配列の場合などは基本構文通り、第二引数には関数を設定する。
public static void main(String[]args) {Stream<Integer>stream = Arrays.stream(new Integer[] { 1, 2, 3, 4});int total = stream.reduce(10, (accum, value)->accum + value);// total=20System.out.println("total=" + total);}