整数のリストをJavaストリームと合計する方法は?


364

整数のリストを合計したい。次のように動作しますが、構文が正しくありません。コードを最適化できますか?

Map<String, Integer> integers;
integers.values().stream().mapToInt(i -> i).sum();

5
「しかし、構文は正しくないと思います」どう思いますか?これは通常のイディオムです。mapToLongマップに設定できる値によっては、オーバーフローを回避するために使用したい場合があります。
Alexis C.

3
@JBNizet i -> i個人的には非常に明確です。ええ、はい、値は自動的にボックス化解除されることを知っておく必要がありますが、Java 5以降はそうです...
Alexis C.

4
@AlexisC。mapToInt()に渡されるため、そして私は経験豊富な開発者なので、理解できます。しかし、i-> iは、コンテキストなしでは何もしないように見えます。Integer :: intValueはより冗長ですが、ボックス化解除操作を明示的にします。
JBニゼット2015年

1
@JBNizetメソッドを呼び出す人は、呼び出すたびにfoo(int i)書き込みを行いませんfoo(myInteger.intValue());(または少なくとも私は期待していません!!)。Integer::intValueもっと明白だと私は同意しますが、ここでも同じことが当てはまると思います。人々は一度それを学べばそれで終わりです:-)。それは魔法の難読化のようではありません。
Alexis C.

4
@JB Nizet:まあ、i -> iノーオペレーションのように見え、概念的にノーオペレーションです。確かに、内部でInteger.intValue()呼び出されますが、内部でさらに深くなると、そのメソッドはインライン化されて、ソースコードのように動作しないようになります。Integer::intValueバイトコードに合成メソッドを作成しないというボーナスポイントがありますが、ソースコードをどのように編成するかについての決定を促進するものではありません。
Holger

回答:


498

これは機能しますが、i -> iいくつかの自動ボックス化解除が行われているため、奇妙に「感じられます」。次のいずれかが機能し、コンパイラーが元の構文で内部で何を行っているかをより適切に説明します。

integers.values().stream().mapToInt(i -> i.intValue()).sum();
integers.values().stream().mapToInt(Integer::intValue).sum();

2
BigInteger :)があるとどうなりますか?
GOXR3PLUS

13
簡単なオプションの1つは、BigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
マシュー

158

さらに2つのオプションを提案します。

integers.values().stream().mapToInt(Integer::intValue).sum();
integers.values().stream().collect(Collectors.summingInt(Integer::intValue));

2つ目はCollectors.summingInt()コレクターsummingLong()を使用していますが、で使用するコレクターもありますmapToLong


そして3番目のオプション:Java 8は非常に効果的な LongAdderは、並列ストリームとマルチスレッド環境での要約を高速化するように設計さアキュムレーターをます。ここでは、使用例を示します。

LongAdder a = new LongAdder();
map.values().parallelStream().forEach(a::add);
sum = a.intValue();

86

ドキュメントから

縮約操作縮約操作(フォールドとも呼ばれる)は、一連の入力要素を取り、一連の数値の合計または最大値を見つける、または要素をリスト。ストリームクラスには、reduce()およびcollect()と呼ばれる複数の一般的なリダクション操作の形式と、sum()、max()、count()などの複数の特殊なリダクション形式があります。

もちろん、そのような操作は、次のように単純な順次ループとして簡単に実装できます。

int sum = 0;
for (int x : numbers) {
   sum += x;
}

ただし、上記のような変異累積よりも削減操作を選択するのには十分な理由があります。リダクションは「より抽象的」であるだけでなく、個々の要素ではなくストリーム全体で動作しますが、要素の処理に使用される関数が関連付けられている限り、適切に構築されたリダクション操作は本質的に並列化可能ですそして無国籍。たとえば、合計を求めたい数のストリームを指定すると、次のように書くことができます。

int sum = numbers.stream().reduce(0, (x,y) -> x+y);

または:

int sum = numbers.stream().reduce(0, Integer::sum);

これらのリダクション操作は、ほとんど変更することなく、安全に並行して実行できます。

int sum = numbers.parallelStream().reduce(0, Integer::sum);

したがって、マップには次を使用します。

integers.values().stream().mapToInt(i -> i).reduce(0, (x,y) -> x+y);

または:

integers.values().stream().reduce(0, Integer::sum);

2
OPの方がはるかに優れており、明確でもあります。このコードには、ボックス化解除とボックス化の操作全体が含まれます。
JBニゼット2015年

1
@JBNizetエスケープ分析がボクシングを排除しない限り。あなたはそれができるかどうかを確認するためにそれを試す必要があります。
Peter Lawrey、2015年

6
(x、y)-> x + yは、xとyのボックス化を解除し、それらを合計して、結果をボックス化する必要があります。そして、ストリームの次の要素で結果を追加し、何度も何度も繰り返します。
JBニゼット2015年

3
Integer :: sumにも同じ問題があります。また、mapToInt()を使用してIntStreamを作成する場合、sum()を呼び出すのは、reduce()を呼び出すよりも簡単です。
JB

3
docs.oracle.com/javase/8/docs/api/java/lang/…を参照してください。Integer.sum()の2つの引数はint型です。したがって、ストリームからの2つの整数は、メソッドに引数として渡すためにボックス化解除する必要があります。メソッドはintを返しますが、reduce()はBinaryOperator <Integer>を引数として取るため、Integerを返します。したがって、合計の結果は整数にボックス化する必要があります。
JBニゼット2015年

28

あなたはreduceメソッドを使うことができます:

long sum = result.stream().map(e -> e.getCreditAmount()).reduce(0L, (x, y) -> x + y);

または

long sum = result.stream().map(e -> e.getCreditAmount()).reduce(0L, Integer::sum);

9
にはすでにそのようなアキュムレータがありますint。それはInteger::sum
Alex Salauyou

1
あなたはロングを返すので、それよりLong::sumも良いでしょうInteger::sum
Andrei Damian-Fekete


11

collectメソッドを使用して、整数のリストを追加できます。

List<Integer> list = Arrays.asList(2, 4, 5, 6);
int sum = list.stream().collect(Collectors.summingInt(Integer::intValue));

6

これは、intタイプ配列(long配列LongStreamdouble配列DoubleStreamなど)を合計する最も短い方法です。Streamただし、すべてのプリミティブ整数型または浮動小数点型に実装があるわけではありません。

IntStream.of(integers).sum();

残念ながら、int配列はありません。だから、IntStream.of()私たちがこのような何かの不気味を作っている限り、この問題のための作業は、しませんIntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
カプラン

必要ありませんintegers.values().stream().mapToInt( Integer::intValue ).sum()。これで十分です。
Sachith Dickwella

3

リストにオブジェクトがある人を助けることができますように。

オブジェクトのリストがあり、このオブジェクトの特定のフィールドを合計したい場合は、以下を使用してください。

List<ResultSom> somList = MyUtil.getResultSom();
BigDecimal result= somList.stream().map(ResultSom::getNetto).reduce(
                                             BigDecimal.ZERO, BigDecimal::add);

ありがとうございます。私の
Scenerio

1

整数のリストを宣言しました。

ArrayList<Integer> numberList = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));

以下のさまざまな方法を使用してみてください。

使用する mapToInt

int sum = numberList.stream().mapToInt(Integer::intValue).sum();

使用する summarizingInt

int sum = numberList.stream().collect(Collectors.summarizingInt(Integer::intValue)).getSum();

使用する reduce

int sum = numberList.stream().reduce(Integer::sum).get().intValue();

-1
class Pojo{
    int num;

    public Pojo(int num) {
        super();
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

List<Pojo> list = new ArrayList<Pojo>();
            list.add(new Pojo(1));
            list.add(new Pojo(5));
            list.add(new Pojo(3));
            list.add(new Pojo(4));
            list.add(new Pojo(5));

            int totalSum = list.stream().mapToInt(pojo -> pojo.getNum()).sum();
            System.out.println(totalSum);

-1

ほとんどの側面がカバーされています。ただし、Integer、Long(専用のストリームサポートがすでに存在する)以外のその他のデータ型の集約を見つける必要がある場合があります。たとえば、BigIntegerを使用したstramの場合、このようなタイプの場合、次のような削減操作を使用できます。

list.stream()。reduce((bigInteger1、bigInteger2)-> bigInteger1.add(bigInteger2))

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.