关于Java集合的流操作,整理一下加深印象。

本文最后更新于:3 个月前

前言

一些碎碎念,这是我的标准格式。

不说几句废话,感觉写博客就失去了灵魂,说一下最近吧,最近在开发一个基本的中国式报表,被里面的逻辑着实恶心到了,我们先来看一下报表的需求是什么样子的。
一个报表

里面的数据全部需要在看不清的也没关系,不太需要看清喔,如果想看清的话,可以留个评论并留个联系方式,我发给你瞅一瞅。话说也不是很复杂喔,在中国式报表中,这种一般般啦,这里面需要填的数只有18列,剩下的所有就都需要通过这18列来计算得出,行于行需要计算,列于列也需计算。由于数据并不是一次性导入,所以只能把计算逻辑放在server层里面。就是显示的时候来计算。具体是有点复杂的。逐渐偏离主题,就是在这里我用到了一些Stream流的一些东西加上之前也用到,所以就在这里做一个总结归纳

Stream的由来

太复杂我也不想说,打字有点累,我们正常处理集合里的数据时,第一想到的是不是就是for循环嘞,别问,问就是循环大法。确实,面向对象的编程思维,让我们第一时间想到的就是一个一个循环然后进行一些过滤,查找,分类操作。在Java没有流之前,大家伙都这么干为什么Java8要推出流这个东西呢,显而易见,肯定之前的循环大法再进行这种操作时弊端太多,太垃圾啦。又或者借鉴了JavaScript的高阶函数。说起流还是得说说Lambda表达式嘛,如果没有Lambda表达式也不会有这玩意。Lambda表示就是就是函数式接口,让Java这个严格的面向对象的编程语言,产生了微妙的化学反应。如果一个人先学JavaScript的再来学Java,我能感受到他内心的崩溃,满嘴fack。好家伙,过滤个集合,还要new一个新集合,然后还要遍历,Java在编程语言上的的神话,是不是闹呢。确实早些年Java为了代码的严谨以及可读性,牺牲了太多。但这,也是Java成功的原因。

Stream流的用法

话不多说,直接上代码,

1
2
3
4
5
6
7
8
9
10


/**
* 代码就是最好的文章
*/
public class LocalDateTimeDemoTest {


public static void main(String[] args) {
ArrayList<User> list = getUserList();

过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
// 过滤
// 年龄大于10的
List<User> collect = list.stream().filter(user -> user.getAge() > 10).collect(Collectors.toList());
// 名字最后是2的
List<User> collect1 = list.stream().filter(user -> user.getUserName().endsWith("2")).collect(Collectors.toList());
// 组合过滤
List<User> collect2 = list.stream().filter(user -> user.getAge() > 10).filter(user -> user.getUserName().endsWith("2")).collect(Collectors.toList());

// 查找名为王0的,如果有重名的取第一个,如果为空则初始化一个(这种写法主要是让程序更加健壮,避免的空指针),你也可以在orElse(这里写null,但是你写null的意义不是很大喔)
User user1 = list.stream().filter(user -> user.getUserName().equals("王0")).findFirst().orElse(new User());

// 这种也是获取第一个,这个的效率比上面的高,一般查找唯一数据,用这个比较快。
User user2 = list.stream().filter(user -> user.getUserName().equals("王0")).findAny().orElse(new User());

排序

1
2
3
4
5
6

// 按年龄从小到大排序
List<User> collect7 = list.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());

// 按年龄从大到小排序
List<User> collect8 = list.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());

分组

1
2
3
4
5
6
7
8
9
10
11
12
// 分组
// 以班级分组, 返回一个map,key为部门ID,value为分组的集合
Map<Integer, List<User>> collect3 = list.stream().collect(Collectors.groupingBy(User::getGradeId));

// 多级分组 这个就比较好玩了,Map里面嵌套Map,哈哈和上面一样,先以班级分组,然后再按年龄分组
// 外层的Map的Key为班级ID,里面的Map的Key为年龄。
Map<Integer, Map<Integer, List<User>>> collect4 = list.stream().collect(Collectors.groupingBy(User::getGradeId, Collectors.groupingBy(User::getAge)));

// 分组加汇总,以班级为单位总薪酬
// Map的Key 为班级ID,Value为分过组后的总和,注意这里喔,直接用double来进行总和计算,一般来说肯定会发生精度丢失。所以这种这种double运算,一般来说是不可以直接sum的
Map<Integer, Double> collect5 = list.stream().collect(Collectors.groupingBy(User::getGradeId, Collectors.summingDouble(User::getSalary)));

归总

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    // 归总,
// 何为归总,就是结果集是一个。

//求所有人年龄总和(好像没啥意义,)
int sum = list.stream().mapToInt(User::getAge).sum();

// 也可以先过滤班级编号为3的同学再进行年龄求和
int sum1 = list.stream().filter(user -> user.getGradeId() == 3).mapToInt(User::getAge).sum();

// 找到年龄最小的同学
User user = list.stream().min(Comparator.comparing(User::getAge)).orElse(new User());

// 找到年龄最大的同学并输出最小年龄是多少(强行凑代码,哈哈)
int age = list.stream().max(Comparator.comparing(User::getAge)).orElse(new User()).getAge();

// 获取按班级分组后年龄总和最小值是多少(乍一听感觉很绕,实际有啥绕的,自己写了一下,确实有点绕,淦。)
// 实际意义待斟酌啊。。
Integer integer = list.stream().collect(Collectors.groupingBy(User::getGradeId, Collectors.summingInt(User::getAge))).values().stream()
.min(Comparator.comparing(Integer::valueOf)).orElse(0);


// 演示一下BigDecimal的运算吧,差点忘了,
// 啥也不干直接就是算金额总和
BigDecimal bigDecimal = list.stream().map(User::getMoney).reduce(BigDecimal::add).orElse(BigDecimal.valueOf(0));

// 先进行简单的过滤,再进行计算
BigDecimal bigDecimal1 = list.stream().filter(user3 -> user3.getMoney() != null).map(User::getMoney).reduce(BigDecimal::add).orElse(BigDecimal.valueOf(0));

// 计算所有同学的年龄
int sum2 = list.stream().mapToInt(User::getAge).sum();

// 计算Double型的Salary,这里肯定会发生精度丢失,具体怎么处理,大家可以百度一下,提示要想不丢精度,肯定是要用BigDecimal的
double sum3 = list.stream().mapToDouble(User::getSalary).sum();

// 计算年轻平均数
Double collect6 = list.stream().collect(Collectors.averagingDouble(User::getAge));


// 看看姓王的崽子有几个.
long count = list.stream().filter(user3 -> user3.getUserName().startsWith("王")).count();


}

用来测试的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    // 模拟数据组,真是数据远远比这复杂,但是你只需要用关键数据即可。
public static ArrayList<User> getUserList() {
ArrayList<User> list = new ArrayList<>();
list.add(new User(0,"王0",3,22,12.3,BigDecimal.valueOf(1600)));
list.add(new User(1,"王1",3,8,11.7,BigDecimal.valueOf(1300)));
list.add(new User(2,"王2",3,25,16.3,BigDecimal.valueOf(1500)));
list.add(new User(3,"王3",2,17,19.3,BigDecimal.valueOf(1800)));
list.add(new User(4,"王4",2,22,11.3,BigDecimal.valueOf(2344)));
list.add(new User(5,"王5",2,13,15.3,BigDecimal.valueOf(1120)));
list.add(new User(6,"王6",1,8,19.3,BigDecimal.valueOf(9980)));
list.add(new User(7,"王7",1,3,111.3,BigDecimal.valueOf(1112)));
return list;
}
}

模拟数据实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class User {
/**
* 主键Id
*/
private int id;
/**
* 用户名
*/
private String userName;
/**
* 班级ID
*/
private int gradeId;
/**
* 年龄
*/
private int age;

/**
* 薪酬
*/
private double salary;


/**
* 强行增加一个金额,用来演示BigDecimal运算
*/

private BigDecimal money;

public User() {
}

public User(int id, String userName, int gradeId, int age, double salary, BigDecimal money) {
this.id = id;
this.userName = userName;
this.gradeId = gradeId;
this.age = age;
this.salary = salary;
this.money = money;
}
/**
* 省略get,set方法
*/
}


尾声

害小说看多了,不bb两句难受

我再说几句嘿嘿,其实这些方法,你完全不用死记硬背,背了也没啥用喔,得再脑壳里面归类,分组,这些都是我的工具,大部分人的心理都是这样,我就算暂时用不上我也得要有。我就是这种心理,花里胡哨学了一大堆喔,就算我暂时用不上,万一哪天用上了呢,在我万一哪天碰上后,和一个名字都没听的娃,你说上手谁快,学习其实是越学容易的,当年感觉越学越难时,就别硬往下学了,肯定是积累的疑惑太多了。就比如这个Java的流,难嘛?可以说一点都不难,正常人就算只听过这个名字,百度一下,也会用。那问题又来了,那你弄这个汇总是干啥呢,其实是在培养思想喔,在查询数据这块,sql语句天下第一。我想说大家不要小看任何一款数据库软件,如mysql,数据库软件,可以说是人类智慧的结晶,是一群智商顶尖的人,并将数据结构和算法用到极致才开发出来的东西。我整理这个流的操作时,脑子里想的是,正常的一个select * from user **** ;用JavaAPI应该怎么操作,应该都是能对应上的,对应上之后 ,我就越发感觉Sql厉害。

封面原图

我老懂事人了

塞尔菲娅


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!