Java 累加合计,性能测试(初步走入 Java 8 新特性实战)

Java 累加合计,性能测试(初步走入 Java 8 新特性实战)

在做页面 excel 合计的时候,发现的这个问题,想要找到一种快速优雅的写法,大概原因就是因为,嗯我没有眼睛,机器不相信自己的眼睛,那种因为看错,或者随手打错的不易发现的 bug 实在是太烦了,尤其调 sql 时候的 bug 你的眼睛有可能在骗你,所以写出来可读性强性能好的代码能有效减少不易发现的 bug 的产生 . 所以有了以下茴香的茴字有几种写法的神奇代码,当然只是选了几种实现,以我有d限的知识水平来简单测试性能列举,选出我比较喜欢的. 当然,以下计算都绝对精确,用双精度类型 double 来计算,场景就是精确度不需要太高的场景下

几种实现(茴字的同写法)

自定义计数器

这也是我的第一版本计算和的方案,想法是做个通用的,不需要我自己去一个个 new 新的字段存储总合的,具体最后有值加进来应该有程序灵活创建.

实现思路
  1. HashMap 经典工作算法,我用 key 存储字段名称,value 存储累加后的值.
  2. 边界,有那些值是不需要计算累加的,那些是不用累加的,我需要一个黑名单来帮助我过滤条件.
  3. 遍历集合内每一个元素,如果元素在我自定义的累加器(即语言创建的map 中),就取map 的值,然后相加存储回去,如果不在就直接存储.
  4. 转换,数据来回转换,以一种同样的方式存储计算.
最后实现如下
    private static void singleStream(List<Map<String, Object>> calculateList, List<String> ignoreList) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Map<String, Object> sumByType = new HashMap<>();
        for (Map<String, Object> record : calculateList) {
            for (Map.Entry<String, Object> entry : record.entrySet()) {
                // 忽略组合计算清单
                String key = entry.getKey();
                if (ignoreList.contains(key)) {
                    continue;
                }
                Object value = entry.getValue();
                double newValue = Double.parseDouble(value.toString());

                // 计算浮点数类型字段的和
                if (sumByType.containsKey(key)) {
                    // 已有键新增值
                    double oldValue = Double.parseDouble((String) sumByType.get(key));
                    newValue = oldValue + newValue;
                    sumByType.put(key, String.valueOf(newValue));
                } else {
                    sumByType.put(key, value.toString());
                }

            }
        }
    }

当然实现了基本功能,但是有问题,我的 list 只能是 map 类型的,如果换成具体实体类还得重新写,换一个更加通用化的算法,并且,在每一次从map 里存储数据拉取数据都耗费时间,这段文字的时候我注意到如果将最后自己的累加器定义成 Map<String,Double> 类型会更快.

存在问题,从算法的角度,应该有更快的方案,就单论这种实现而言ps 本人单薄的算法知识不支持我继续太深入了,本片文章的参考意义在于从茴字的写法打破大家的一些刻板印象

使用单条流一项一项的计算数据

当然这种是我抄来的代码,大概写法就是用了下面的句式,函数式编程的一些特性,当时学的时候简单知道是什么大概怎么做,平时写的代码大概也都是指令式多一些,当然有些用到函数式编程的思想,或者说有些函数式编程的东西很自然的看着就学会了,一些简单的操作也会用流代替循环,但是没有特意看过,只知道流内部迭代,流比循环快. 当我抄代码的时候我懵了,因为一共块 20 个计算项,叠在一起这么写在我当时 流 = 优化的循环看来,就是一个循能解决的问题 20 个循环来做着所以就在想了都用流了能不能就单纯用流搞定那,像我上面循环的写法一样

然后嗯就去看了 Java 8 实战,现在看一半了,看完后写出来了纯流的办法,当理解透了流之后可能会写出更快的,但是现阶段,嗯简单的搞一下对照也够了.

stream().mapToDouble(map -> Double.parseDouble(ToDoubleFunction<? super T> mapper).sum())

HashMap<String, Object> sumByType = new HashMap<>();
sumByType.put("calculated_value_1", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("calculated_value_1").toString())).sum());

流 + 循环 (上面的完善版本)

Don’t repeat yourself (DRY) 原则,让我本能的感觉的了不优雅(主要是对自己的不自信,高强度编码的时候脑子转速快的话,有的时候手打会出现一些小错误,比如 ctrl v 没 v 上,当我长时间盯着电脑看,眼睛也欺骗了我的时候有些隐式 bug 会搞死人的心态),对上面的写法优化了下之后就变成了下面这样.

由两层循环决定,外层循环只要决定那些需要计算的,我需要维护一个待计算列表就好了.

private static HashMap<String, Double> calculateSumByType(List<Map<String, Object>> calculateList, List<String> fields) {
    HashMap<String, Double> sumByType = new HashMap<>();

    for (String field : fields) {
        double sum = calculateList.stream()
                .mapToDouble(map -> Double.parseDouble(map.get(field).toString()))
                .sum();
        sumByType.put(field, sum);
    }
    return sumByType;
}

纯流计算版本

当我看完了版本书之后,跃跃欲试了,开始写下了下面的版本,它用极少的代码优雅的实现了我计算的目的,可读性也是当前最好的,只需要知道大概每一步什么意思可以轻松的看出来要做什么,可维护性极佳.

设计实现

入参 : 1. 要遍历的列表 , 2. 黑名单 list(那些不计算 ps 一般不计算值比计算值少,好维护,列表短,性能会好一些)

实现用到的语法糖简单介绍

推荐兄弟们去看 Jasva 8 实战

  1. stream() : 生成流 (操作数据源)
  2. .flatMap() : 将流抚平成新流 (可以看作原先的流是关起来的擅自,该操作是打开扇子) ,将数据源以指定最小粒度抚平展开,便于计算.
  3. .filter() : 过滤掉不需要的不数据返回一个谓词,true 进入下一层级.
  4. .collect() : 终端操作,流只能使用一次,遇见终端操作被消费. collect 终端操作为收集操作,有些为计算,比如 sum() 也是终端操作.
  5. Collectors.groupingBy() : 分组操作,对收集集合分组,支持多层级分组. 在这个语句中终端操作是通过 分组操作,指定分组时嵌套 summingDouble 来完成的按照不同的 key 去累加收集.
private static void streamGroupingBy(List<Map<String, Object>> simulatedData, List<String> strings) {
        Map<String, Double> sumByType = simulatedData.stream()
                .flatMap(map -> map.entrySet().stream())
                .filter(entry -> strings.stream().noneMatch(b -> entry.getKey().equals(b)))
                .collect(Collectors.groupingBy(
                        Map.Entry::getKey,
                        Collectors.summingDouble(entry -> Double.parseDouble(entry.getValue().toString()))
                ));
    }

并行流版本

此版本为上一版本的优化, 用 parallelStream() 方法,其他操作合上一版本纯属雷同没有巧合,改动及其简单.

在上面的例子中,myList.parallelStream()返回一个并行流,而myList.stream()返回一个串行流。在使用并行流时,你应该注意多线程的安全性和一致性,确保对共享状态的访问是线程安全的。

并行流并不是在所有情况下都比串行流更快,因为它涉及到线程切换和并发控制的开销。在某些情况下,特别是对于较小的数据集或简单的操作,串行流可能更为合适。因此,在使用并行流时,应该进行性能测试,以确定它是否对特定情况产生了优势。

当然啦,用的线程池为 jvm 自动管理的线程池,如果这个线程池有其他功能在使用,我们在计算的时候会影整个系统,所以如果决定使用的话给一个最大线程数是一种更明确的选择,减少突入起来的数据计算影响系统内部其他地方的可能性.

请添加图片描述

定义便量,循环累加

都到了最后,顺手就把最基础的,也就是一开始就会的方案带上吧,虽然它看起来最不优雅,写起来也很是麻烦,但是嗯带着吧(哈哈哈哈哈,其实有个坑啊,到时候就懂了) 当然啦,最朴素的逻辑就放一段,下面详细测试代码里可以看到完整逻辑.

 private static void originalAddition(List<Map<String, Object>> calculateList) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        double calculatedValue1 = 0.0;
        double calculatedValue2 = 0.0;
        for (Map<String, Object> map : calculateList) {
            calculatedValue1 += Double.parseDouble(map.get("calculated_value_1").toString());
            calculatedValue2 += Double.parseDouble(map.get("calculated_value_2").toString());
        }
        HashMap<String, Object> res = new HashMap<>();
        res.put("calculatedValue1", calculatedValue1);
        res.put("calculatedValue2", calculatedValue2);
    }

测试部分

实践是检验真理的唯一标准,测试部分主要有两份测试报告,一份完整的测试代码,和一些反常现象和其背后的一些逻辑组成,当然啦,肯定是能优化的,但是老规矩先挖坑.

先上测试代码,最后给出两份执行的结果分别说一下情况,代码由几部分组成

  1. 调用部分,统一调用测试不同实现效率在不同数量级别下的情况.
  2. 实现部分,列举不同实现
  3. 数据生成部分,因为肯定不能那实际业务代码出来看,也不能用实际上数据,所以我只能模拟一个有着十几列的 list 来完成剩下的部分,他有着需计算总和的部分,和不需要计算总和的部分,模拟实际情况.
  4. 测试形式,注掉不需要的代码,不如我想看过滤不需要累加求和的数据对各种实现的影响,那我就在各个部分注释掉它,在代码中加入 stopWatch 来检测代码的运行实践,已经输出写参数方便我判断,也会通过增加减少参数列来实现模拟我有不同多的参数列,(注释掉,或者新写一个).

测试代码

package com.yidiansishiyi.aimodule.job.once;

import org.springframework.util.StopWatch;

import java.util.*;
import java.util.stream.Collectors;

public class SimulatedDataGenerator {

    static int TEN = 10;
    static int HUNDRED = 100;
    static int THOUSAND = 1000;
    static int TEN_THOUSAND = 10000;
    static int ONE_HUNDRED_THOUSAND = 100000;
    static int MILLION = 1000000;
    static int TEN_MILLION = 10000000;
    static int BILLION = 100000000;

    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(TEN, HUNDRED, THOUSAND, TEN_THOUSAND, ONE_HUNDRED_THOUSAND, MILLION);
        for (int order : integers) {
            List<Map<String, Object>> simulatedData = generateSimulatedData(order);
            System.out.println("数量分组 " + order + " 条下性能报告: (单位毫秒)");
            // 忽略清单, 部分不列举在累加内的参数
            List<String> ignoreList = Arrays.asList("gender", "non_calculable_1", "id", "name", "non-calculable", "non_calculable_2");

            // 流分组计算
            streamGroupingBy(simulatedData, ignoreList);

            // 自定义累加器去计算
            singleStream(simulatedData, ignoreList);

            // 并行流分组计算
            parallelStreamGroupingBy(simulatedData, ignoreList);

            // 单条流去计算
            singleStream(simulatedData);

            // 原始定义变量相加
            originalAddition(simulatedData);

            List<String> actualColumn = Arrays.asList("calculated_value_1", "calculated_value_2", "txt4", "txt2", "txt3", "calculated_value_3", "bonus", "txt1", "salary", "age", "expenses");
            calculateSumByType(simulatedData, actualColumn);

        }
    }

    private static void originalAddition(List<Map<String, Object>> calculateList) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        double calculatedValue1 = 0.0;
        double calculatedValue2 = 0.0;
        double txt4 = 0.0;
        double txt2 = 0.0;
        double txt3 = 0.0;
        double calculatedValue3 = 0.0;
        double bonus = 0.0;
        double txt1 = 0.0;
        double salary = 0.0;
        double age = 0.0;
        double expenses = 0.0;

        for (Map<String, Object> map : calculateList) {
            calculatedValue1 += Double.parseDouble(map.get("calculated_value_1").toString());
            calculatedValue2 += Double.parseDouble(map.get("calculated_value_2").toString());
            txt4 += Double.parseDouble(map.get("txt4").toString());
            txt2 += Double.parseDouble(map.get("txt2").toString());
            txt3 += Double.parseDouble(map.get("txt3").toString());
            calculatedValue3 += Double.parseDouble(map.get("calculated_value_3").toString());
            bonus += Double.parseDouble(map.get("bonus").toString());
            txt1 += Double.parseDouble(map.get("txt1").toString());
            salary += Double.parseDouble(map.get("salary").toString());
            age += Double.parseDouble(map.get("age").toString());
            expenses += Double.parseDouble(map.get("expenses").toString());
        }
        HashMap<String, Object> res = new HashMap<>();
        res.put("calculatedValue1", calculatedValue1);
        res.put("calculatedValue2", calculatedValue2);
        res.put("calculatedValue3", calculatedValue3);
        res.put("txt4", txt4);
        res.put("txt2", txt2);
        res.put("txt3", txt3);
        res.put("txt1", txt1);
        res.put("bonus", bonus);
        res.put("salary", salary);
        res.put("age", age);
        res.put("expenses", expenses);
        stopWatch.stop();
        System.out.print("原始定义变量相加 :");
        System.out.println(stopWatch.getTotalTimeMillis());
        // 打印前 结果
//        sumByType.forEach((key, value) -> System.out.println(key + ":" + value));
    }

    private static HashMap<String, Double> calculateSumByType(List<Map<String, Object>> calculateList, List<String> fields) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        HashMap<String, Double> sumByType = new HashMap<>();

        for (String field : fields) {
            double sum = calculateList.stream()
                    .mapToDouble(map -> Double.parseDouble(map.get(field).toString()))
                    .sum();
            sumByType.put(field, sum);
        }
        stopWatch.stop();
        System.out.print("流加循环 :");
        System.out.println(stopWatch.getTotalTimeMillis());
        return sumByType;
    }


    private static void singleStream(List<Map<String, Object>> calculateList) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        HashMap<String, Object> sumByType = new HashMap<>();
        sumByType.put("calculated_value_1", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("calculated_value_1").toString())).sum());
        sumByType.put("calculated_value_2", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("calculated_value_2").toString())).sum());
        sumByType.put("txt4", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("txt4").toString())).sum());
        sumByType.put("txt2", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("txt2").toString())).sum());
        sumByType.put("txt3", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("txt3").toString())).sum());
        sumByType.put("calculated_value_3", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("calculated_value_3").toString())).sum());
        sumByType.put("bonus", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("bonus").toString())).sum());
        sumByType.put("txt1", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("txt1").toString())).sum());
        sumByType.put("salary", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("salary").toString())).sum());
        sumByType.put("age", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("age").toString())).sum());
        sumByType.put("expenses", calculateList.stream().mapToDouble(map -> Double.parseDouble(map.get("expenses").toString())).sum());
        stopWatch.stop();
        System.out.print("单条流去计算 :");
        System.out.println(stopWatch.getTotalTimeMillis());
        // 打印前 结果
//        sumByType.forEach((key, value) -> System.out.println(key + ":" + value));
    }

    private static void singleStream(List<Map<String, Object>> calculateList, List<String> ignoreList) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Map<String, Object> sumByType = new HashMap<>();
        for (Map<String, Object> record : calculateList) {
            for (Map.Entry<String, Object> entry : record.entrySet()) {
                // 忽略组合计算清单
                String key = entry.getKey();
                if (ignoreList.contains(key)) {
                    continue;
                }
//            double newValue = 0.0;
//            Object value = entry.getValue();
//            // 计算参数转换 (如扩展其他类型数据补全改结构)
//            if (value instanceof BigDecimal) {
//                newValue = ((BigDecimal) value).doubleValue();
//            } else if (value instanceof String) {
//                newValue = Double.valueOf((String) value);
//            } else {
//                continue;
//            }
                Object value = entry.getValue();
                double newValue = Double.parseDouble(value.toString());

                // 计算浮点数类型字段的和
                if (sumByType.containsKey(key)) {
                    // 已有键新增值
                    double oldValue = Double.parseDouble((String) sumByType.get(key));
                    newValue = oldValue + newValue;
                    sumByType.put(key, String.valueOf(newValue));
                } else {
                    sumByType.put(key, value.toString());
                }

            }
        }
        stopWatch.stop();
        System.out.print("自定义累加器去计算 :");
        System.out.println(stopWatch.getTotalTimeMillis());
        // 打印前 结果
//        sumByType.forEach((key, value) -> System.out.println(key + ":" + value));
    }

    // 流计算
    private static void streamGroupingBy(List<Map<String, Object>> simulatedData, List<String> strings) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Map<String, Double> sumByType = simulatedData.stream()
                .flatMap(map -> map.entrySet().stream())
                .filter(entry -> strings.stream().noneMatch(b -> entry.getKey().equals(b)))
                .collect(Collectors.groupingBy(
                        Map.Entry::getKey,
                        Collectors.summingDouble(entry -> Double.parseDouble(entry.getValue().toString()))
                ));
        stopWatch.stop();
        System.out.print("流分组计算 :");
        System.out.println(stopWatch.getTotalTimeMillis());
        // 打印前 结果
//        sumByType.forEach((key, value) -> System.out.println(key + ":" + value));
    }

    private static void parallelStreamGroupingBy(List<Map<String, Object>> simulatedData, List<String> strings) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Map<String, Double> sumByType = simulatedData.parallelStream()
                .flatMap(map -> map.entrySet().stream())
                .filter(entry -> strings.stream().noneMatch(b -> entry.getKey().equals(b)))
                .collect(Collectors.groupingBy(
                        Map.Entry::getKey,
                        Collectors.summingDouble(entry -> Double.parseDouble(entry.getValue().toString()))
                ));
        stopWatch.stop();
        System.out.print("并行流分组计算 :");
        System.out.println(stopWatch.getTotalTimeMillis());
        // 打印前 结果
//        sumByType.forEach((key, value) -> System.out.println(key + ":" + value));
    }

    // mork 数据生成部分
    private static List<Map<String, Object>> generateSimulatedData(int dataSize) {
        List<Map<String, Object>> data = new LinkedList<>();

        for (int i = 0; i < dataSize; i++) {
            Map<String, Object> record = new HashMap<>();
            record.put("id", i);
            record.put("name", "Record_" + i);
            record.put("age", getRandomValue(18, 60));
            record.put("gender", getRandomGender());
            record.put("salary", getRandomValue(30, 80));
            record.put("expenses", getRandomValue(10, 50));
            record.put("bonus", getRandomValue(0, 100));
            record.put("txt1", getRandomValue(0, 100));
            record.put("txt2", getRandomValue(0, 100));
            record.put("txt3", getRandomValue(0, 100));
            record.put("txt4", getRandomValue(0, 100));
            record.put("non_calculable_1", "Some non-calculable data");
            record.put("non_calculable_2", "Another non-calculable data");

            // 计算项
            double calculatedValue1 = (double) record.get("salary") - (double) record.get("expenses");
            double calculatedValue2 = (double) record.get("salary") + (double) record.get("bonus");
            double calculatedValue3 = (double) record.get("expenses") / (double) record.get("age");

            record.put("calculated_value_1", calculatedValue1);
            record.put("calculated_value_2", calculatedValue2);
            record.put("calculated_value_3", calculatedValue3);

            data.add(record);
        }

        return data;
    }

//    private static double getRandomValue(double min, double max) {
//        double randomValue = min + (max - min) * new Random().nextDouble();
//        // 保留两位小数
//        return Math.round(randomValue * 100.0) / 100.0;
//    }


    private static final Random random = new Random();

    private static double getRandomValue(double min, double max) {
        double randomValue = min + (max - min) * random.nextDouble();
        // 保留两位小数
        return Math.round(randomValue * 100.0) / 100.0;
    }

    private static String getRandomGender() {
        return new Random().nextBoolean() ? "Male" : "Female";
    }
}

测试中情况说明

先说结论

​ 不管在测试集中哪个数据量情况下,从效果上看,代码最少,看起来最清晰(熟悉流语法后)完全使用流版本竟然出乎意料的是最慢的,反而最普通的原始的丑陋的,循环中写 += 的反而是最快的(当然在并行流不出手的情况下,好吧是有点耍流氓,并行流不是流吗,哈哈哈哈哈). 这一结论打败了我 流效率永远优于循环 的刻板印象 (当然也是我算法不到家,以后肯定能写出来更快的) .

​ 其次写法贼抽象的 使用单条流一项一项的计算数据 竟然在我最后写循环调用之前反而是第二快的(也没算并行流),果然 码不可貌相 ,甚至当只有一个值需要计算的时候,第二种写法在 Stream 的优化先是快于循环的,果然流的内部迭代还是优化过的.不过在这个写法外面套一层循环后效率比这一条条写快,而且出错的概率还低.

​ 直接以白名单去计算的 循环 + 流 和 硬编码的,在没有需要过滤的条件下和需要过滤的条件下没有影响,而剩下的需要判别的或多或少有些影响,

​ 并行流在处理 1000 条这样数据以上,有着明显的优势,代码简单,速度最快.

原因

​ 上学的时候数据结构与算法的老师说过看时间复杂度,就数循环数,看空间复杂度,就看它有没有开辟新的空间,一般程序可以在时间和空间上进行转换(有些流畅的代码源码非常大,也巨耗存储,嗯我记得有些比赛作品就挺抽象,不过最牛逼的还是 超级马里奥,将节省进行到底),从现象上看很容易 将流和循环画上等号,但是想起老师最开始讲的时间复杂度计算方式, 计算语句频度,求极限值,这才为现象画上了等号,流绝非等价于循环是循环的特殊化,先看下流最初的定义,可能和我一样,有好多人没有关心过.

Java 中的流(Stream)是一种用于对集合进行处理的高级抽象。它提供了一种更为函数式的编程风格,允许你通过一系列的操作来处理数据,如过滤、映射、排序等。

流的核心思想是将数据处理操作串联起来,形成一个流水线。你可以将多个中间操作连接起来,最后添加一个终端操作来触发实际的计算。这样的设计使得流可以进行懒加载,只有在终端操作被触发时,中间操作才会被执行。

当然,流的不同操作看起来是一次循环,但是流有自己的处理方式,将不同操作合并到同一次遍历中了(我们把这种技术叫作循环合并)。

​ 当然其中肯定有优化的空间,但是要更加深入了解一些原理.

结果

​ 不过从结果上来看,如何快速的写出简单易懂,不令人头大的代码好像已经有了结论,即使不用并行流它处理速度在大数据量下会慢一些,但是也绝对够用,能节省很多编码时间,以及在系统开发运维中可能一不小心带来的错误,没有防止四海皆准的真理,我们的一切都在现有条件的定义下才是正确的, 流不是万用银弹,但是在很多场合下是对开发有好处的,虽然需要一点时间入门,嗯等我看完那本书应该算是入门了,哈哈哈哈.这个坑是我填起来最快的,因为赶到这了,正好.

​ Java 8 实战,不仅仅有流的知识,函数式编程等 Java 8 特性也在其中挺好看的,当然需要一定的前置基础,不过嗯很有意思,当然和小说没得比.偶尔还是令人头大.

测试报告

数量分组 10 条下性能报告: (单位毫秒)
流分组计算 :62
自定义累加器去计算 :2
并行流分组计算 :5
单条流去计算 :4
原始定义变量相加 :0
流加循环 :0
数量分组 100 条下性能报告: (单位毫秒)
流分组计算 :10
自定义累加器去计算 :4
并行流分组计算 :3
单条流去计算 :2
原始定义变量相加 :1
流加循环 :1
数量分组 1000 条下性能报告: (单位毫秒)
流分组计算 :44
自定义累加器去计算 :26
并行流分组计算 :12
单条流去计算 :13
原始定义变量相加 :6
流加循环 :8
数量分组 10000 条下性能报告: (单位毫秒)
流分组计算 :135
自定义累加器去计算 :125
并行流分组计算 :19
单条流去计算 :49
原始定义变量相加 :40
流加循环 :38
数量分组 100000 条下性能报告: (单位毫秒)
流分组计算 :717
自定义累加器去计算 :866
并行流分组计算 :142
单条流去计算 :408
原始定义变量相加 :280
流加循环 :396
数量分组 1000000 条下性能报告: (单位毫秒)
流分组计算 :12512
自定义累加器去计算 :8803
并行流分组计算 :1553
单条流去计算 :3994
原始定义变量相加 :2724
流加循环 :3811
Process finished with exit code 0

总结

填坑部分,填掉了如何计算 合计 部分

jvm 调优 ,测数据的时候有些语句太贫乏 , GC 爆了

请添加图片描述

函数式编程,虽说简单的和思想已经学了,但是熟练使用,和那些 Java 类库现有的 api 还不熟

计算精度问题,如果引入计算精度,流现有的 api 已经不满足了,得自定义,属于流的高级用法了

找类库,对我相信,这个代码一定被前辈写过了,因为场景还是相对频繁的,我应该能抄到或者是直接用现成的代码.

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值