Stream的由来

我们正常处理集合里的数据时,第一想到的是不是就是for循环,别问,问就是for循环大法。确实,面向对象的编程思维,让我们第一时间想到的就是一个一个循环然后进行一些过滤,查找,分类操作。在Java没有这个流之前,大家伙都这么做的。

为什么Java8要推出流这个东西呢,肯定是这种方法有优越性。之前的循环大法从最终的结果来看和流操作的结果是一样的,都可以完成功能。流的操作来源于Lambda表达式,如果没有Lambda表达式也不会有这个东西。使用流可以提升代码的简洁度,这个提升是质的飞跃,以前十几行代码甚至更多,现在可能只需要一行代码就能完成,这是就有不同的意见了,说这种代码难以维护,我感觉完成同样的功能,代码越少是越容易维护的,一个三百行代码的方法和一个三十行的方法,维护成本不是一个量级的。

Stream流的用法

1
2
3
4
5
6

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
14
15
16
17
18
// 过滤
// 年龄大于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的,如果有重名的取第一个,如果为空则初始化一个(这种写法主要是让程序更加健壮,避免的空指针)。
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
7
8
9
10

// 按年龄从小到大排序
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
13
14
15
16
// 分组
// 以班级分组, 返回一个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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    // 归总,
// 何为归总,就是结果集是一个。

//求所有人年龄总和(好像没啥意义,)
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();

// anyMatch表示,判断的条件里,任意一个元素成功,返回true
// 这里就是如果同学中有一个小于六岁的则返回true
boolean b = list.stream().anyMatch(i -> i.getAge() < 6);

// allMatch表示,判断条件里的元素,所有的都是,返回true
// 判断同学里面是不是所有人的年龄都小于六十岁,如果都小于六十岁则返回true
boolean b1 = list.stream().allMatch(i -> i.getAge() < 60);

// noneMatch跟allMatch相反,判断条件里的元素,所有的都不是,返回true
// 判断同学的年龄是不是都不大于六十岁,如果有一个大于六十岁 返回false
boolean b2 = list.stream().noneMatch(i -> i.getAge() > 60);


}

用来测试的数据

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方法
*/
}


其实这些方法,不用死记硬背,需要的时候查询一下就行了,当用的多了自然而然的就记住了。死记硬背,当时是背下来了,不常用到的话,也会很快的遗忘。

实际上在查询数据这方面,sql是全球第一的。不要小看任何一款数据库软件。数据库软件,可以说是人类智慧的结晶,是一群智商顶尖的人,并将数据结构和算法用到极致才开发出来的东西。一个几百兆的体积的数据库软件可以毫不费力的管理大于自身几百倍体积的数据。这种能力是真正编程的美丽。

我在整理这个流的操作时,如果我们把集合看成数据库里的一行行数据,Java这个对集合的流固然强大,可和SQL一比,也能理解,毕竟闻道有先后,术业有专攻。