如何用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.15年

4
@AlexisC。这是可以理解的,因为它已传递给mapToInt(),并且因为我是一位经验丰富的开发人员。但是我->我,没有上下文,看起来像个小问题。Integer :: intValue更为冗长,但是使拆箱操作更为明确。
JB Nizet

1
@JBNizet调用该方法的foo(int i)人不会在foo(myInteger.intValue());每次调用该方法时都写(或者至少我希望不会!)。我同意你的观点,Integer::intValue但我认为同样适用于此。人们应该只学习一次,然后就完成了:-)。这不像是某种神奇的迷惑。
亚历克西斯(Alexis C.)

4
@JB Nizet:好吧,i -> i看起来像是无人驾驶,从概念上讲,这无人驾驶。当然,在幕后Integer.intValue()被调用,但在幕后更深,该方法被内联成为恰恰变成了源代码中的无操作。Integer::intValue优点是不要在字节码中创建合成方法,但这不是决定如何组织源代码的决定因素。
Holger 2015年

Answers:


498

这将起作用,但是i -> i正在执行一些自动拆箱操作,这就是为什么它“感到”奇怪的原因。以下任何一种方法都可以工作,并且可以使用您的原始语法更好地解释编译器在做什么:

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

2
如果我们有一个BigInteger :)怎么办?
GOXR3PLUS

13
一个简单的选择是BigDecimal sum = numbers.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
Matthew

158

我建议另外2种选择:

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

第二个使用Collectors.summingInt()收集器,还有一个summingLong()您可以与使用的收集器mapToLong


第三种选择: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;
}

但是,有充分的理由倾向于使用归约运算,而不是像上面这样的变异累积。归约不仅是“更抽象的”(它在整个流上而不是在单个元素上运行),而且,只要用于处理元素的函数具有关联性,正确构​​造的reduce操作就具有固有的可并行性。无国籍 例如,给定一个我们想求和的数字流,我们可以这样写:

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 Nizet

1
@JBNizet除非逸出分析消除了拳击操作。您将不得不尝试看看是否可以。
彼得·劳瑞

6
(x,y)-> x + y需要取消对x和y的装箱,求和,然后将结果装箱。并一次又一次地将结果与流的下一个元素相加。
JB Nizet

3
Integer :: sum也遇到同样的问题。如果使用mapToInt()具有IntStream,则在其上调用sum()比调用reduce()更简单。
JB Nizet

3
请参阅docs.oracle.com/javase/8/docs/api/java/lang/…。Integer.sum()的两个参数的类型为int。因此,必须将流中的两个Integer拆箱,以作为参数传递给方法。该方法返回一个int,但是reduce()使用BinaryOperator <Integer>作为参数,因此返回一个Integer。因此,总和的结果必须装箱成整数。
JB Nizet

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类型数组的最短方法(对于longarray LongStream,对于doublearray DoubleStream等等)。Stream尽管并非所有原始整数或浮点类型都具有实现。

IntStream.of(integers).sum();

不幸的是我们没有任何int数组。因此IntStream.of(),除非我们做出类似以下的鬼事,否则将不会解决该问题:IntStream.of( integers.values().stream().mapToInt( Integer::intValue ).toArray() ).sum();
Kaplan

不用了,这就足够了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);

由于它帮助我在我之情况之一
A_01

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此类,我们可以使用reduce操作,例如

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.