文章目录
前言
之前,针对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}]
如果再数据库中,此时的数据格式类似下面这种形式:
month | name | age |
---|---|---|
一月 | xj1 | 10 |
一月 | xj11 | 10 |
一月 | xj111 | 10 |
二月 | xj2 | 10 |
转换形式
在一般开发中,我们通常期望返回数据样式如下:
此时,通常会采取一对多
方式,进行数据的映射,保证查询到的数据返回上面的格式信息。
但
定义 一对多映射结果集 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}]}}