为什么java收集流每次运行两次getter?

时间:2017-07-21 12:56:07

标签: java performance java-8 java-stream collectors

我有一个包含项目的列表,其中每个项目都是一个对象,其中包含我感兴趣的getter方法。我想在完整列表上运行以总结所有这些getter结果。

当我使用java 8流时,它看起来像这样:

double currentProduction = itemList.stream().collect(
    Collectors.summingDouble((e) -> e.getProduction(param)));

在普通的旧java中,它看起来像这样:

for (Item item : itemList) {
    currentProduction += item.getProduction(param);
}

两种方法产生完全相同的结果,但我的记录器报告,对于每个项目实例,在java 8流解决方案的情况下,getProduction()方法运行TWICE。在普通的旧java列表迭代解决方案中,getProduction方法只是按预期运行一次。

由于getProduction方法成本很高,这对我来说是一个问题。

这是为什么?我能做些什么(除了只使用for循环)?

2 个答案:

答案 0 :(得分:12)

Collectors.summingDouble的实施存在错误。

您可以改为使用itemList.stream().mapToDouble(Class1::getProduction).sum()

错误详情:

Collector实施的来源。

    return new CollectorImpl<>(
            () -> new double[3],
            (a, t) -> { sumWithCompensation(a, **mapper.applyAsDouble(t)**);
                        a[2] += **mapper.applyAsDouble(t)**;},
            (a, b) -> { sumWithCompensation(a, b[0]);
                        a[2] += b[2];
                        return sumWithCompensation(a, b[1]); },
            a -> computeFinalSum(a),
            CH_NOID);

错误由**标记。他们两次调用mapper.applyAsDouble(t)

答案 1 :(得分:4)

这里有一个小MCVE来展示错误报告JDK-8151123中解决的问题,该问题在talex

的答案中提到
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SO {
    static class CoffeeBabe {
        final Double value;
        CoffeeBabe(Double value) {
            this.value = value;
        }
        Double getProduction() {
            System.out.println("getProduction() for " + this.value);
            return value;
        }
    }

    public static void main(String[] args) {
        List<CoffeeBabe> itemList = Arrays.asList(
                new CoffeeBabe(13.0),
                new CoffeeBabe(14.0),
                new CoffeeBabe(15.0)
        );
        System.out.println("mapToDouble...");
        double sum = itemList.stream()
            .mapToDouble(CoffeeBabe::getProduction).sum();
        System.out.println("sum = " + sum);

        System.out.println("\nCollectors.summingDouble");
        sum = itemList.stream().collect(Collectors.summingDouble(
            CoffeeBabe::getProduction));
        System.out.println("sum = " + sum);
    }
}

输出

mapToDouble...
getProduction() for 13.0
getProduction() for 14.0
getProduction() for 15.0
sum = 42.0

Collectors.summingDouble
getProduction() for 13.0
getProduction() for 13.0
getProduction() for 14.0
getProduction() for 14.0
getProduction() for 15.0
getProduction() for 15.0
sum = 42.0