2016-11-12 9 views
5

私は好奇心が強い、どのようにjava8ストリームで複数の変数を集計するのですか?java8ストリームの合計倍数

Integer wCPU = 0; 
Double wnetwork = 0.0; 
Double wMem = 0.0; 

this.slaContractList.forEach(sla -> { 
    wCPU += sla.getNumberOfCPUs(); 
    wnetwork += sla.getNetworkBandwith(); 
    wMem += sla.getMemory(); 
}); 

しかし、これはラムダ式の変数が最終的であるためコンパイルされません。

+1

カスタムコレクタをしたい、見て:アキュムレータと原子、これらの2の間の選択は、別の質問ですhttp://stackoverflow.com/questions/37187541/java-8-stream-add-elements-to-list-and-sum/37188002#37188002 – Tunaki

+0

ハックの弱点は何ですか?それは素敵なコード行が少ないようです。 –

+1

あなたはそれを言った...それはハックだ。並列に実行すると、 '+ ='のように '++ 'が[アトミックな操作ではありません](http://stackoverflow.com/questions/25168062/why-is-i)ので正しい出力が得られない可能性があります-not-atomic)。 – Tunaki

答えて

7

SlaContractオブジェクトのリストであり、それはSlaContract(numberOfCPUs, networkBandwith, memory)することができますコンストラクタがあります。

SlaContract sumContract = slaContractList.stream() 
    .reduce(new SlaContract(0, 0.0, 0.0), (sla1, sla2) -> { 
     return new SlaContract(sla1.getNumberOfCPUs() + sla2.getNumberOfCPUs(), sla1.getworkBandwith() + sla2.getworkBandwith(), sla1.getMemory() + sla2.getMemory()); 
    }); 

Double wnetwork = sumContract.getworkBandwith(); 
Double wMem = sumContract.getMemory(); 
Integer wCPU = sumContract.getNumberOfCPUs(); 

同じ溶液を、単純なクラスのために:

Point sumPoint = pointsList.stream() 
    .reduce(new Point(0, 0), (p1, p2) -> { 
     return new Point(p1.x + p2.x, p1.y + p2.y); 
    }); 
+3

'reduce'は蓄積中に入力パラメータを変更してはいけません。これは並行して実行されるときに破損します。常に積み上げを含む新しいオブジェクトを返さなければなりません。 [参照](http://stackoverflow.com/questions/23869930/is-the-accumulator-of-reduce-in-java-8-allowed-to-modify-its-arguments)。ここで 'collect'を使いたいと思っています。 – Tunaki

+1

編集された回答をご覧ください。 –

6

Stream.reduceStream.sumを使用してみてください:

Double wnetwork = slaContractList.stream() 
      .mapToDouble(sla -> sla.getNetworkBandwith()) 
      .sum(); 

Double wMem = slaContractList.stream() 
      .mapToDouble(sla -> sla.getMemory()) 
      .sum(); 

Integer wCPU = slaContractList.stream() 
      .mapToInt(sla -> sla.getNumberOfCPUs()) 
      .sum(); 

は、ストリームを使用する利点は、代わりにstream()の使用parallelStream()のオプションですhttps://docs.oracle.com/javase/tutorial/collections/streams/reduction.html

を参照してください。状況によっては、単純なループよりも効率的である場合があります。

+0

なので、属性ごとにマップを使用しますか?これは単一のループほど遅くないのですか? –

+0

基本的にタプルのストリームです。タプルの各値が別々に集計されても、ループの1回のパス内であれば、それは速くなると思います。 –

+0

これはストリームを合計できる方法です。ストリームを使用しない他の人とこのソリューションを比較してください。 'stream()'の代わりに 'parallelStream()'をチェックしてください。 –

2

は、私はこのような簡単なハックをするでしょう:

Integer[] wCPU = new Integer[1]; 
    Double[] wnetwork = new Double[1]; 
    Double[] wMem = new Double[1]; 

    this.slaContractList.forEach(sla -> { 
     wCPU[0] += sla.getNumberOfCPUs(); 
     wnetwork[0] += sla.getNetworkBandwith(); 
     wMem[0] += sla.getMemory(); 
    }); 

それは、彼らが効果的に最終概念を導入していたJava 8のように、両方のためのfinalキーワードを持っているオプションです。つまり、あなたは一度だけ割り当てられたことを意味します。 slaContractListを想定し

+0

私はあなたのハックが好きです;) –

+0

おめでとうございます。しかし、私は他の答えがより良い/ハックではないと思う。 –

+0

これらは「概念的に」変更可能な整数と倍精度ですが、唯一の問題はスレッドセーフではありませんが、スレッドには問題がありません。 – pdem

2

だけで合計を行うには、 Łukasz答えのようなストリームの合計演算を使用しますが、 "最終的な問題"を解決するためのより一般的な解決法では、java.util.concurrent.atomicのクラスを使用できます。それはストリームで使用されることが意図され、スレッドセーフであるため、パラレルストリームで使用できます。

AtomicInteger wCPU = new AtomicInteger(); 
DoubleAccumulator wnetwork = new DoubleAccumulator(Double::sum,0.d); 
DoubleAccumulator wMem = new DoubleAccumulator(Double::sum,0.d); 

this.slaContractList.forEach(sla -> { 
    wCPU.addAndGet(sla.getNumberOfCPUs()); 
    wnetwork.accumulate(sla.getNetworkBandwith()); 
    wMem.accumulate(sla.getMemory()); 
}); 

は今、あなたは、実装の2種類があることを参照してください。

java 8 : Are LongAdder and LongAccumulator preferred to AtomicLong?

+0

これは、追加のコンストラクタを作成するよりもクリーンであるようです。 –

関連する問題