web-dev-qa-db-ja.com

reduce()メソッドはJava 8でどのように機能しますか?

reduce()メソッドが Java-8 でどのように機能するかを理解しようとしています。

たとえば、私はこのコードを持っています:

public class App {

    public static void main(String[] args) {
        String[] arr = {"lorem", "ipsum", "sit", "amet"};
        List<String> strs = Arrays.asList(arr);

        int ijk = strs.stream().reduce(0, 
            (a, b) -> { 
                System.out.println("Accumulator, a = " + a + ", b = " + b);
                return a + b.length();
            },
            (a, b) -> {
                System.out.println("Combiner");
                return a * b;
            });
        System.out.println(ijk); 
    }
}

そして出力はこれです:

Accumulator, a = 0, b = lorem
Accumulator, a = 5, b = ipsum
Accumulator, a = 10, b = sit
Accumulator, a = 13, b = amet
17

これらの文字列の長さの合計です。また、コンバイナにはアクセスしないため、数値が乗算されず、数値が追加されるだけです。

しかし、streamparallelStreamに置き換えた場合:

int ijk = strs.parallelStream().reduce(0, 
    (a, b) -> { 
        System.out.println("Accumulator, a = " + a + ", b = " + b);
        return a + b.length();
    },
    (a, b) -> {
        System.out.println("Combiner");
        return a * b;
    });

System.out.println(ijk); 

これは出力です:

Accumulator, a = 0, b = ipsum
Accumulator, a = 0, b = lorem
Accumulator, a = 0, b = sit
Combiner
Accumulator, a = 0, b = amet
Combiner
Combiner
300

アキュムレータとコンバイナの両方にアクセスしていますが、返されるのは乗算のみです。では、合計はどうなりますか?

22
user9608350

あなたは言うreduceのドキュメントを読むべきです:

さらに、コンバイナ機能はアキュムレータ機能と互換性がなければなりません。すべてのuとtについて、次の条件を満たす必要があります。

combiner.apply(u、accumulator.apply(identity、t))== accumulator.apply(u、t)

あなたの場合、あなたはその法則を破っています(accumulatorsumを実行し、combinermultiplicationを実行します)。このような操作は実際には定義されておらず、基になるソースのSpliteratorがどのように実装されているかによって異なります(実行しないでください)。

さらに、combineronlyで、並列ストリームに対して呼び出されます。

もちろん、アプローチ全体を次のように簡略化できます。

Arrays.asList("lorem", "ipsum", "sit", "amet")
      .stream()
      .mapToInt(String::length)
      .sum();

学習目的でそれを行っている場合、正しいreduceは(sumを取得するため)です。

strs.parallelStream()
    .reduce(0,
            (a, b) -> {
                  System.out.println("Accumulator, a = " + a + ", b = " + b);
                  return a + b.length();
            },
            (a, b) -> {
                  System.out.println("Combiner");
                  return a + b;
            });
20
Eugene

重要な概念:アイデンティティ、アキュムレータ、コンバイナ

Stream.reduce()operation:操作の参加要素を個別のブロックに分解してみましょう。そうすれば、それぞれが果たす役割をより簡単に理解できます

  • Identity –縮約操作の初期値である要素、およびストリームが空の場合のデフォルトの結果
  • itemAccumulator – 2つのパラメーターを取る関数:リダクション操作の部分的な結果とストリームの次の要素
  • Combiner – 2つのパラメーターを取る関数:リダクション操作の部分的な結果とストリームの次の要素Combiner – リダクション操作の部分的な結果を組み合わせるときに使用される関数削減が並列化されている場合、またはアキュムレータ引数のタイプとアキュムレータ実装のタイプの間に不一致がある場合

ストリームが並列実行されると、Javaランタイムはストリームを複数のサブストリームに分割します。そのような場合、関数を使用してサブストリームの結果を1つに結合する必要があります。これはコンバイナの役割

Case 1: CombinerはparallelStreamで動作し、例に示されています

ケース2:引数のタイプが異なるアキュムレータの例

この場合、Userオブジェクトのストリームがあり、アキュムレータ引数のタイプはIntegerとUserです。ただし、アキュムレータの実装は整数の合計であるため、コンパイラはユーザーパラメータのタイプを推測できません。

List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());

コンパイルエラー

The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> {})

この問題は、コンバイナを使用して修正できます。これはメソッド参照Integer::sumまたはラムダ式を使用して(a,b)->a+b

int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);

簡単に言うと、シーケンシャルストリームを使用し、アキュムレータ引数のタイプとその実装のタイプが一致する場合、コンバイナを使用する必要はありません。

12
Deadpool

Java-stream を使用して削減する方法は3つあります。簡単に言うと、_Stream::reduce_は2つの結果アイテム(または最初のアイテムのID値1)から始まり、それらのアイテムを使用して操作を実行して、新しい値を減らします。次の項目ごとに同じことが起こり、値が減らされて操作が実行されます。

_'a'_、_'b'_、_'c'_および_'d'_のストリームがあるとします。削減は、次の一連の操作を実行します。

  1. result = operationOn('a', 'b')-operationOnは何でもかまいません(入力の長さの合計..)
  2. result = operationOn(result, 'c')
  3. result = operationOn(result, 'd')
  4. _result is returned_

メソッドは次のとおりです。

8
Nikolas

正確に何が起こるかを確認するためのデモとして、加算と乗算を行うことを選択したと思います。

すでにお気づきのように、またすでに述べたように、コンバイナは並列ストリームでのみ呼び出されます。

つまり、並列ストラムでは、ストリームの一部(それぞれ、基になるSpliterator)が切り取られ、別のスレッドによって処理されます。いくつかの部品が処理された後、それらの結果はコンバイナで結合されます。

あなたの場合、4つの要素はすべて別のスレッドで処理され、要素ごとに結合されます。そのため、(0 +以外の)追加が適用されず、乗算のみが表示されます。

ただし、意味のある結果を得るには、*から+に切り替えて、より意味のある出力を行う必要があります。

2
glglgl