Stream——采取collect进行数据Collectors.groupingBy数据分组

本文详细介绍了如何使用Java 8的Stream API进行数据处理,包括数据筛选、判空处理、数据分组等操作。通过示例展示了如何对自定义对象集合进行分组,并提供了多种遍历和自定义分组逻辑的方法,适用于实际开发中的数据转换需求。
摘要由CSDN通过智能技术生成

前言

之前,针对Stream链式编程中的几个方法做了大致的说明。详情可以参考:

JDK 1.8 新特性之Stream 详解个人笔记

但实际业务中,总会存在很多复杂的思维,需要使用到Stream,此时玩的不熟练总感觉无从下手。

以几个案例的形式,展示一些使用流来实现的操作,做一个笔记的记录。

定义数据接收类

因为要考虑使用集合,需要使用到自定义类对象,所以先从定义对象开始。

public class TestVo {
	// 需要根据 month 进行分组
    private String month;
    private String name;
    private BigDecimal age;

    public TestVo(String month, String name, BigDecimal age) {
        this.month = month;
        this.name = name;
        this.age = age;
    }

    public TestVo() {
    }

    // get / set 方法
    // toString 方法 
}

定义测试数据

有了数据接收对象,现在开始定义数据集合信息,有如下几组数据信息:

List<TestVo> user = new ArrayList<>();
TestVo obj1 = new TestVo();
obj1.setMonth("一月");
obj1.setName("xj1");
obj1.setAge(BigDecimal.TEN);
user.add(obj1);

TestVo obj2 = new TestVo();
obj2.setMonth("一月");
obj2.setName("xj11");
obj2.setAge(BigDecimal.TEN);
user.add(obj2);

TestVo obj3 = new TestVo();
obj3.setMonth("一月");
obj3.setName("xj111");
obj3.setAge(BigDecimal.TEN);
user.add(obj3);

TestVo obj4 = new TestVo();
obj4.setMonth("二月");
obj4.setName("xj2");
obj4.setAge(BigDecimal.TEN);
user.add(obj4);

System.out.println( user.toString());

此时的集合数据格式如下所示:

[TestVo{month='一月', name='xj1', age=10}, 
TestVo{month='一月', name='xj11', age=10}, 
TestVo{month='一月', name='xj111', age=10}, 
TestVo{month='二月', name='xj2', age=10}]

如果再数据库中,此时的数据格式类似下面这种形式:

monthnameage
一月xj110
一月xj1110
一月xj11110
二月xj210

转换形式

在一般开发中,我们通常期望返回数据样式如下:
在这里插入图片描述
此时,通常会采取一对多方式,进行数据的映射,保证查询到的数据返回上面的格式信息。

定义 一对多映射结果集 resultmap性能较低!

Stream 中存在一种新的方式,一样能实现上述的功能。
资料参考:Springboot——Mybatis实现一对一、一对多查询

接下来就来说明:

如何采取Stream进行数据格式的转换!

数据筛选

这里时模拟数据,采取手动定义,所以数据一定存在

如果数据是从数据库中查询,此时这里的 users 集合 可能为 null

当出现null.stream()时,会出现NPE报错信息!为了保证代码的健壮性,需要对数据信息进行判空。

1、数据集合判空

常见的stream判空有两种。

1.1、Optional.isPresent()

System.out.println("-----> " + Optional.ofNullable(null).isPresent());
System.out.println("-----> " + Optional.ofNullable(new ArrayList<>()).isPresent());

结果:

-----> false
-----> true

可以通过Optional.ofNullable(集合别名).isPresent()判断集合是否存在,再通过返回 boolean 判断是否继续向下执行链式编程代码。

1.2、orElse 替换

还有一种方式,是如果存在空对象,则将空对象进行替换操作。如下所示:

System.out.println(Optional.ofNullable(user).orElse(new ArrayList<>()));
System.out.println(Optional.ofNullable(null).orElse(new ArrayList<>()));

结果:

[TestVo{month='一月', name='xj1', age=10}, TestVo{month='一月', name='xj11', age=10}, TestVo{month='一月', name='xj111', age=10}, TestVo{month='二月', name='xj2', age=10}]

[]

2、数据分组

和数据库数据分组一样,也是采取的group by,只是采取Stream流的形式。

Map<String, List<TestVo>> collect = Optional.ofNullable(user)
            .orElse(new ArrayList<>())
            .stream()
            .collect(Collectors.groupingBy(TestVo::getMonth));

执行后,数据结果格式如下所示:

{一月=[
	TestVo{month='一月', name='xj1', age=10},
	 TestVo{month='一月', name='xj11', age=10}, 
	 TestVo{month='一月', name='xj111', age=10}
 ], 
二月=[TestVo{month='二月', name='xj2', age=10}]}

完美实现上述功能!

关于这种格式遍历技能扩展

方式一:entrySet()方式遍历

for (Map.Entry<String, List<TestVo>> stringListEntry : collect.entrySet()) {
    System.out.println(stringListEntry.getKey() );
    System.out.println(stringListEntry.getValue());
    System.out.println("====================");
    System.out.println("####################");
}

在这里插入图片描述

方式二:jdk8的Lambda

collect.forEach((month,testVos)->{
    System.out.println(month);
    System.out.println("====================");
    System.out.println(testVos);
    System.out.println("####################");
});

在这里插入图片描述

自定义groupBy 逻辑

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Test5 {
    public static void main(String[] args) {
        List<User2> users = new LinkedList<>();
        users.add(new User2("Jim", 12));
        users.add(new User2("John", 18));
        users.add(new User2("Tom", 21));
        users.add(new User2("Leo", 30));
        users.add(new User2("Kate", 44));
        users.add(new User2("Lio", 50));

        Map<String, List<User2>> tripleUsers = users.stream()
                .collect(Collectors.groupingBy((Function<User2, String>) user -> {
                    String key;
                    if (user.getAge() <= 30) {
                        key = "less30";
                    }else {
                        key = "more30";
                    }
                    return key;
                }, Collectors.toList()));

        System.out.println(tripleUsers);
    }
}
class User2{
    private String name;
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public User2(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

执行后,控制台打印结果如下所示:

{more30=[xj.test.streams.User2@15aeb7ab, xj.test.streams.User2@7b23ec81], 

less30=[xj.test.streams.User2@6acbcfc0, xj.test.streams.User2@5f184fc6, xj.test.streams.User2@3feba861, xj.test.streams.User2@5b480cf9]}

2022.07.19 多个参数分组

public class Test2 {
    public static void main(String[] args) {
        List<TestVo> user = new ArrayList<>();
        TestVo obj1 = new TestVo();
        obj1.setMonth("一月");
        obj1.setName("xj1");
        obj1.setAge(BigDecimal.TEN);
        user.add(obj1);

        TestVo obj2 = new TestVo();
        obj2.setMonth("一月");
        obj2.setName("xj1");
        obj2.setAge(BigDecimal.TEN);
        user.add(obj2);

        TestVo obj3 = new TestVo();
        obj3.setMonth("一月");
        obj3.setName("xj111");
        obj3.setAge(BigDecimal.TEN);
        user.add(obj3);

        TestVo obj4 = new TestVo();
        obj4.setMonth("二月");
        obj4.setName("xj2");
        obj4.setAge(BigDecimal.TEN);
        user.add(obj4);
        
        // 先根据month分组,再将分组后的结果按照name分组
        Map<String, Map<String, List<TestVo>>> collect = Optional.ofNullable(user)
           .orElse(new ArrayList<>())
           .stream()
           .collect(Collectors.groupingBy(TestVo::getMonth,
           Collectors.groupingBy(TestVo::getName)));
           
        System.out.println(collect);
    }
}

运行后的结果如下所示:

{一月=
	{xj111=[TestVo{month='一月', name='xj111', age=10}],
	 xj1=[TestVo{month='一月', name='xj1', age=10}, 
	 	  TestVo{month='一月', name='xj1', age=10}]},
二月=
	{xj2=[TestVo{month='二月', name='xj2', age=10}]}}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值