今天要讲的内容非常重要 – Stream 流。这个 ” 流 ” 可不是我们认为的 ”IO 流 ”,它能够让我们对集合,数组等数据在不改变数据源的情况下,做一些连续性丝滑的操作。你可以理解为,Stream 流它就像是工厂中的流水线一样,一个产品的生成过程,它是需要经过一道道的工序,最后才能完工。而每一道工序,就是在上一道工序的基础上完成的。而且 Stream 流内部大量使用了函数式接口,所以它还能与 Lambda 表达式配合使用,非常强大,高效简洁。
在正式讲之前,为了让大家感受下 Stream 流与 Lambda 表达式配合使用的强大之处,我先给大家写一个应用例子。
Stream 流的初体验
假设,我们现在有一种人员 List 表,这张表储存了每个人的姓名,然后我现在要提三个需求。
- 需求 1:筛选出姓吴的人
- 需求 2: 筛选出名字长度为 3 的人
- 需求 3: 遍历输出所有人的名字
// 人员表
ArrayList<String> peoples = new ArrayList<>();
peoples.add("张柯");
peoples.add("吴耿伟");
peoples.add("张小三");
peoples.add("李四");
peoples.add("刘德华");
peoples.add("吴亦凡");
peoples.add("杨幂");
peoples.add("吴能");
不使用 Stream 流的实现方式:
ArrayList<String> peoples1 = new ArrayList<String>();
for (String people : peoples) {
// 名字姓吴且长度为 3
if (people.startsWith("吴") && people.length() == 3)
peoples1.add(people);
}
for (String people : peoples1) { //
System.out.println(people);
}
使用 Stream 流的实现方式:
peoples.stream()
.filter(s -> s.startsWith("吴") && s.length() == 3)
.forEach(s -> System.out.println(s));
运行结果:
吴耿伟
吴亦凡
可以看到,使用 Stream 流的实现方式,代码非常简洁。
Stream 流的操作通常分为两种: 中间操作 和 终端操作。中间操作可以返回多种流,每种流只能使用一次;终端操作就是对流进行使用,使用完之后返回一个新的集合或数组。且中间操作是惰性执行的,只有终端操作开始时,它才会被执行。
2,获取 Stream 流
在使用 Stream 流之前,我们需要先获取 Stream 流,对于集合和数组来说,它们获取流的方式各不相同。
对于数组,获取 Stream 可以使用:
Arrays.stream();
Stream.of();
对于集合,则可以直接调用 stream()方法。且其内部也是调用的 Arrays.stream();
ArrayList<String> peoples = new ArrayList<>();
peoples.add("张柯");
..........
peoples.stream(); // 获取 Stream 流
3, 操作 Stream 流
Stream 流给我们提供了很多操作方法,如 filter,count, distinct, concat, limit, skip, max, min, findFirst, findAny 等方法。下面我会介绍部分方法使用方式。
a. filter()方法
filter()方法可以对流中的元素进行过滤,筛选出我们想要的元素。
Stream<String> datas = Stream.of("张三", "李四", "卢本伟", "张开成", "李文斌", "张杨叶");
// 使用 filter 方法筛选出姓李的人
datas.filter(s -> s.startsWith("李"))
.forEach(s -> System.out.println(s));
运行结果 : 李四, 李文斌
b. count()方法
count()方法可以对流内的元素的个数进行统计,使用方法也非常简单。
Stream<String> datas = Stream.of("张三", "李四", "卢本伟", "张开成", "李文斌", "张杨叶");
long countResult = datas.filter(s -> s.startsWith("李")).count();
System.out.println(countResult);
运行结果: 2。即两个姓李的人
c. max()和 min()方法
max()和 min()这个相信大家都不陌生,从字面上的意思就是获取流中元素的最大最小元素。
Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);
可以看到,max()和 min()都是根据 Comparator 返回流中的最大最小元素。
Comparator比较器
这里可以顺便讲一下 comparator 比较器,comparator 是一个自定义比较器,比较逻辑在抽象方法 compare 中实现。compare 对 o1 和 o2 进行比较,返回三种状态:
若 compare 返回 0,则 o1=o2;
若 compare 小于 0,则 o1<o2;
若 compare 大于 0,则 o1>o2;
int compare(T o1, T o2);
comparator 会对前两个元素进行比较,然后每次取最大的那个值跟下一个元素进行比较。
此外,comparator 还提供了默认方法 comparing()。comparing 方法可以从给定函数中提取出要比较的字段,然后生成并返回一个新的 comparator 比较器。
Stream<Student> datas1 = Stream.of( //
new Student("Lujia",20,"男"),
new Student("Jacker",17,"女"),
new Student("Killer",34,"男")
);
Optional<Student> max = datas1.max(Comparator.comparing((s) -> { //
return s.getAge(); //}));
System.out.println(max.get().getAge());
d. limit()和 skip()方法
这两个简单讲一下。limit()方法可以对流元素进行截取,取出前 N 个元素,而 skip()方法则是跳过前 N 个元素。下面给大家看一下使用例子。
Stream<String> datas = Stream.of("张三", "李四", "卢本伟", "张开成", "李文斌", "张杨叶");
datas.limit(3).forEach(s -> System.out.println(s));
运行结果: “ 张三 ”, “ 李四 ”, “ 卢本伟 ”
e. concat()方法
concat()是 Stream 中的一个方法。如果你有多个流并且希望都合并成一个流,那你可以使用 concat()实现。
Stream<String> datas = Stream.of("张三", "李四", "卢本伟", "张开成", "李文斌", "张杨叶");
Stream<String> datas2 = Stream.of("文在闫","丽里","凯文");
Stream<String> newStream = Stream.concat(datas, datas2);
newStream.forEach(System.out::println);
f. distinct()方法
distinct()方法可以对流中的元素进行去重操作,它是通过调用对象中的 hashcode 和 equals 方法实现的去重。
Stream<String> datas = Stream.of("张三", "李四", "卢本伟", "张开成", "李文斌", "张杨叶","李文斌");
datas.distinct().forEach(System.out::println);
重复多余的名字 “ 李文斌 ” 会被去除。
若流中的元素是一个对象,则你需要重写类中的 hashcode 和 equals 方法
g. findFirst()和 findAny()方法
findFirst()和 findAny()方法可以查找流中的元素。findFirst()查找的是流中的第一个元素,而 findAny()从流中取出任意一个元素。
Stream<String> datas = Stream.of("张三", "李四", "卢本伟", "张开成", "李文斌", "张杨叶","李文斌");
Optional<String> first = datas.findFirst();
System.out.println(first);
运行结果: Optional[张三]
本博文今天就讲这么多吧!如果你有任何问题,欢迎在评论区提问。