2016-08-23 6 views
2

の平均ます:JAVAJavaの8:複数の属性以下のクラスで

Long fooSum, barSum; 
for(HelloWorld hw: helloWorldList){ 
    fooSum += hw.getFoo(); 
    barSum += hw.getBar(); 
} 
Long fooAvg = fooSum/helloWorldList.size(); 
Long barAvg = barSum/helloWorldList.size(); 

: 私はList<HelloWorld> helloWorldList

@Data 
public class HelloWorld { 
    private Long foo; 
    private Long bar; 
} 

OPTION 1にfooとbarの平均値を取得したいですオプション2:JAVA8

Double fooAvg = helloWorldList.stream().mapToLong(HelloWorld::foo).average().orElse(null); 
Double barAvg = helloWorldList.stream().mapToLong(HelloWorld::bar).average().orElse(null); 

どのアプローチが良いですか? これらの値を取得する方法はありますか?

回答編集:この質問は、重複としてマークされているが、bradimusからのコメントを読んだ後、私はこれを実装することになった:

import java.util.function.Consumer; 
public class HelloWorldSummaryStatistics implements Consumer<HelloWorld> { 
    @Getter 
    private int fooTotal = 0; 
    @Getter 
    private int barTotal = 0; 
    @Getter 
    private int count = 0; 

    public HelloWorldSummaryStatistics() { 
    } 

    @Override 
    public void accept(HelloWorld helloWorld) { 
     fooTotal += helloWorld.getFoo(); 
     barTotal += helloWorld.getBar(); 
     count++; 
    } 

    public void combine(HelloWorldSummaryStatistics other) { 
     fooTotal += other.fooTotal; 
     barTotal += other.barTotal; 
     count += other.count; 
    } 

    public final double getFooAverage() { 
     return getCount() > 0 ? (double) getFooTotal()/getCount() : 0.0d; 
    } 

    public final double getBarAverage() { 
     return getCount() > 0 ? (double) getBarTotal()/getCount() : 0.0d; 
    } 

    @Override 
    public String toString() { 
     return String.format(
      "%s{count=%d, fooAverage=%f, barAverage=%f}", 
      this.getClass().getSimpleName(), 
      getCount(), 
      getFooAverage(), 
      getBarAverage()); 
    } 
} 

メインクラス:

HelloWorld a = new HelloWorld(5L, 1L); 
    HelloWorld b = new HelloWorld(5L, 2L); 
    HelloWorld c = new HelloWorld(5L, 4L); 
    List<HelloWorld> hwList = Arrays.asList(a, b, c); 
    HelloWorldSummaryStatistics helloWorldSummaryStatistics = hwList.stream() 
      .collect(HelloWorldSummaryStatistics::new, HelloWorldSummaryStatistics::accept, HelloWorldSummaryStatistics::combine); 
    System.out.println(helloWorldSummaryStatistics); 

注:示唆したように高精度が必要な場合は他にBigIntegerなどを使用できます。

+2

注:不要なオブジェクトの割り当てを避けるために、代わりに 'Long'の' long'を使用した場合最初のものは非常に良いだろう。また、整数除算に注意してください。 –

+0

最初の方が良いのは、リストを1回だけインターラストするからです。 – f1sh

+4

'Stream'を使用する場合は、独自の[reduce](https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html)を記述してください([' Averager'] (https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html#collect)例) – bradimus

答えて

0

どのアプローチが良いですか?

「より良い」と言うと、「サンプルの真の平均に近い」または「より効率的な」という意味ですか?効率性が目標である場合、ストリームは無視されることが多いかなりのオーバーヘッドを伴います。ただし、これらは可読性とコンサイスコードを提供します。最大限にしようとしているもの、データセットの大きさなどによって異なります。

多分質問に言い換えてください。

2

これまでの回答/コメントでは、ストリームベースのソリューションの利点の1つについて言及していません。stream()parallelStream()に変更するだけで、すべてをマルチスレッドソリューションに変えることができます。

"オプション1"でこれを試してください。それがどれくらいの仕事を必要とするかを見てください。

もちろん、それは「カバーの背後で起こっていることがCPUサイクルを犠牲にしている」という点でさらに「オーバーヘッド」を意味します。 大きなデータセットについて話しているのであれば、実際にあなたに利益をもたらすかもしれません。

少なくとも、parallelStreams()を有効にすると実行時間にどのように影響するかを簡単に確認できます。

1

整数のリストで平均値を見つけたい場合は、古典的アプローチを繰り返し使用する方がよいでしょう。 ストリームにはオーバーヘッドがあり、JVMはストリーム使用のためにクラスをロードする必要があります。しかし、JVMには多くの最適化機能を備えたJITもあります。

不適切なbanchmarkingに注意してください。 JMH ストリームは、反復操作が2つの整数の合計のような単純なものではない場合に効果的です。 ストリームでもコードを並列化できます。並列化がシングルスレッドより優れている場合は、直接の基準はありません。私の場合、関数呼び出しが100ms以上かかる場合、それを並列化することができます。

だから、データセットの処理がかかる場合でない場合は、> 100msのはparallelStream

を試してみてください - 反復処理を使用しています。

P.S.ダグ・リー - 「When to use parallel streams

関連する問題