私は勧告が削減を使用したいが、それは良いアイデアである理由おそらくより完全な答えは説明します。
ラムダ式では、ラムダ式が定義されている範囲にある変数output
を参照できますが、値を変更することはできません。その理由は、内部的にコンパイラはラムダを本体として持つ新しい関数を作成することでラムダを実装できるようにする必要があります。コンパイラは、この生成された関数で使用されるすべての値がパラメータリストで使用できるように、必要に応じてパラメータを追加することを選択できます。あなたのケースでは、そのような関数はラムダの明示的なパラメータtestEnum
を持っているはずですが、ラムダ本体のローカル変数output
も参照するため、生成された関数の2番目のパラメータとして追加することができます。実際には、コンパイラはラムダからこの関数を生成することがあります:
private void generatedFunction1(TestEnum testEnum, int output) {
output |= testEnum.getValue();
}
あなたが見ることができるように、output
パラメータは、発信者が使用するoutput
変数のコピーである、とOR操作はコピーのみに適用されます。元のoutput
変数は変更されないため、言語設計者は暗黙的にラムダに渡された値の変更を禁止することに決めました。
は削減の使用がはるかに優れたアプローチである瞬間のためともかく、最も直接的な方法で問題を回避するには、ラッパーで
output
変数(サイズ1または例えば
int[]
配列をラップすることができ
AtomicInteger
。ラッパーの参照は生成された関数に渡され、
output
の内容を更新するので、
output
の値ではなく、
output
が実質的にfinalのままであるため、コンパイラーは不平を言わない。たとえば、次のように
AtomicInteger output = new AtomicInteger();
setOfEnums.stream().forEach(testEnum -> (output.set(output.get() | testEnum.getValue()));
か、我々はのAtomicIntegerを使用していることから、我々としても今、私たちは「はあなたが後で
AtomicInteger output = new AtomicInteger();
setOfEnums.stream().forEach(testEnum -> (output.getAndUpdate(prev -> prev | testEnum.getValue())));
、
Stream
並列に使用することを選択した場合には、それはスレッドセーフにすることあなたが尋ねたことに最も似ている答えを見て、還元を使用する優れた解決策について話すことができます。
は、ステートレス削減(reduce()
、およびステートフル削減(collect()
が)。、、違いを視覚化ハンバーガーを提供するコンベアベルトを考慮することStream
によって提供される削減の2種類があり、あなたの目標は、にハンバーガーパティのすべてを収集することですステートフル・リダクションでは、新しいハンバーガー・バンで始まり、各ハンバーガーからパティを取り出して収集し、それを収集するためにセットアップしたハンバーガー・バンの中のパティのスタックに加えます。ステートレスリダクションでは、空のハンバーガーパン(「アイデンティティ」と呼ばれます。その空のハンバーガーパンは、コンベアベルトが空の場合に終わるものですから)、各ハンバーガーがベルトに着くと、以前に蓄積したバーガーのコピーを取り出し、新しいものからパテを追加します。以前に蓄積されたハンバーガーを捨てる。
ステートレスリダクションは膨大な廃棄物のように見えるかもしれませんが、累積値をコピーするのが非常に安い場合があります。そのようなケースの1つは、プリミティブ型のコピーが非常に安価であるため、加算、ORingなどのアプリケーションでプリミティブを処理するときにステートレスな縮小が理想的です。
ステートレス縮小を使用すると、 :
setOfEnums.stream()
.mapToInt(TestEnum::getValue) // or .mapToInt(testEnum -> testEnum.getValue())
.reduce(0, (resultSoFar, testEnum) -> resultSoFar | testEnum);
熟考するいくつかのポイント:forループ
- あなたの元には、あなたのセットが非常に大きく、あなたが並列ストリームを使用し、おそらく場合を除いて、ストリームを使用するよりも、おそらく高速です。ストリームを使用するためにストリームを使用しないでください。彼らが理にかなっているならば、それを使う。
- 私の最初の例では、私は
Stream.forEach()
の使用を示しました。 Stream
を作成してforEach()
に電話するだけであれば、コレクションのforEach()
に直接電話する方が効率的です。
- あなたは
Set
の種類について言及していませんでしたが、EnumSet<TestEnum>
を使用していることを願っています。これはビットフィールドとして実装されているため、他の種類のSet
よりもはるかに優れています(O(1))。 EnumSet.noneOf(TestEnum.class)
は不変の減少だが、あなたは `acc`(int型であることを)変更される
あなたは '.reduce(0、(e1、e2) - > e1 | e2)'の形式でIDを提供することができます。または、代わりに 'get'の代わりに' orElse(0) 'を実行してください。 – Eugene
@Eugeneはい、空のSetofEnumsのようなsitutationの方が良いでしょう。 –