Java使用groupingBy分组数据乱序问题解决方案

业务背景

假设当前有一个对学生进行各方面评价的对象,评价包括考察模块、考察点、评价备注。字段关系为,同一模块包含多个考察点,每个考察点有一个评价备注。
对象代码如下
/**
 * 评价信息对象
 */
@Data
@AllArgsConstructor
class Evaluation {

    /**
     * 考查模块
     */
    private String inspectionModule;

    /**
     * 考察点
     */
    private String inspectionPoint;

    /**
     * 评价备注
     */
    private String remark;
}

接下来我们做一些测试数据,看起来会直观一些
    public static void main(String[] args) {
        List<Evaluation> list = new ArrayList<>() {{
            add(new Evaluation("课上表现", "专注力", "上课的时候很专注"));
            add(new Evaluation("课上表现", "表达能力", "表达能力还可以"));
            add(new Evaluation("逻辑思维", "清晰度", "思路比较清晰"));
            add(new Evaluation("逻辑思维", "敏捷程度", "反应有点迟钝"));
            add(new Evaluation("课后作业", "卷面分", "卷面干净整节"));
            add(new Evaluation("课后作业", "正确率", "大约90%的样子"));
        }};
        list.forEach(System.out::println);
    }
这里我们把创建的测试对象集合打印出来(控制台打印信息如下)

在这里插入图片描述

看图我们可以更加直观地看到我们数据是1个模块2个考察点这样的关系,这个时候,我们想要按照考察模块对数据进行分组。很简单,使用stream中的groupingBy即可。

技术实现

我们直接上代码
    public static void main(String[] args) {
        List<Evaluation> list = new ArrayList<>() {{
            add(new Evaluation("课上表现", "专注力", "上课的时候很专注"));
            add(new Evaluation("课上表现", "表达能力", "表达能力还可以"));
            add(new Evaluation("逻辑思维", "清晰度", "思路比较清晰"));
            add(new Evaluation("逻辑思维", "敏捷程度", "反应有点迟钝"));
            add(new Evaluation("课后作业", "卷面分", "卷面干净整节"));
            add(new Evaluation("课后作业", "正确率", "大约90%的样子"));
        }};
        list.forEach(System.out::println);
        Map<String, List<Evaluation>> map = list.stream().collect(Collectors.groupingBy(Evaluation::getInspectionModule));
        map.forEach((s, evaluations) -> System.out.println(s + ": " + evaluations));
    }
运行结果是怎样的呢?(控制台打印信息如下)

在这里插入图片描述

数据确实根据考察模块进行了聚合,但是它的顺序却不是按照我们事先安排好的顺序,那么原因是什么呢?我们在数据聚合成map之后,打印一下这个map的信息即可知道原因。
System.out.println(map.getClass().getSimpleName());
打印出来的信息是“HashMap”,原因找到了,因为聚合后的数据是用HashMap来收集的,所以打乱了原本的顺序。

在这里插入图片描述

查看源码我们也能看出来,groupingBy默认使用HashMap来收集数据。因此我们只需要把HashMap换成LinkedHashMap即可解决这个问题。

在这里插入图片描述

通过阅读源码我们也能看到,groupingBy本身也是支持我们去自定义一个Map实例去收集数据的。

代码优化

    public static void main(String[] args) {
        List<Evaluation> list = new ArrayList<>() {{
            add(new Evaluation("课上表现", "专注力", "上课的时候很专注"));
            add(new Evaluation("课上表现", "表达能力", "表达能力还可以"));
            add(new Evaluation("逻辑思维", "清晰度", "思路比较清晰"));
            add(new Evaluation("逻辑思维", "敏捷程度", "反应有点迟钝"));
            add(new Evaluation("课后作业", "卷面分", "卷面干净整节"));
            add(new Evaluation("课后作业", "正确率", "大约90%的样子"));
        }};
        list.forEach(System.out::println);
        Map<String, List<Evaluation>> map = list.stream().collect(Collectors.groupingBy(Evaluation::getInspectionModule, LinkedHashMap::new, Collectors.toList()));
        map.forEach((s, evaluations) -> System.out.println(s + ": " + evaluations));
    }
运行结果如下

在这里插入图片描述

Finish

题外话

优化聚合后的数据,增强数据可读性
    public static void main(String[] args) {
        //创建测试数据
        List<Evaluation> list = new ArrayList<>() {{
            add(new Evaluation("课上表现", "专注力", "上课的时候很专注"));
            add(new Evaluation("课上表现", "表达能力", "表达能力还可以"));
            add(new Evaluation("逻辑思维", "清晰度", "思路比较清晰"));
            add(new Evaluation("逻辑思维", "敏捷程度", "反应有点迟钝"));
            add(new Evaluation("课后作业", "卷面分", "卷面干净整节"));
            add(new Evaluation("课后作业", "正确率", "大约90%的样子"));
        }};
        //打印集合
        list.forEach(System.out::println);
        //根据考察模块进行数据聚合
        Map<String, List<Evaluation>> map = list.stream().collect(Collectors.groupingBy(Evaluation::getInspectionModule, LinkedHashMap::new, Collectors.toList()));
        //打印map实现类
        System.out.println(map.getClass().getSimpleName());
        //打印聚合后的数据
        map.forEach((s, evaluations) -> System.out.println(s + ": " + evaluations));
        //数据可读性优化
        StringBuilder msg = new StringBuilder();
        map.forEach((s, evaluations) -> {
            msg.append(s).append(":\r\n");
            evaluations.forEach(inspectDetail -> msg.append(inspectDetail.getInspectionPoint()).append(": ")
                    .append(inspectDetail.getRemark()).append("\r\n"));
        });
        System.out.println(msg);
    }
运行结果如下

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值