目录
一 什么是Stream流
stream流操作是Java 8提供一个重要新特性,它允许开发人员以声明性方式处理集合,其核心类库主要改进了对集合类的 API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中,能 让代码更加简洁,极大地简化了集合的处理操作,提高了开发的效率和生产力。
二 Stream流的区别
stream流是抽象的,并且它关注的是整体的情况,而不是某个单独的部分,所以当你构建一个程序的时候,有时候你的只是写了一堆,逻辑上如果这个这么做,如果那个不这么做,或者使用Switch语句啊,创建一个集合等等,这么做就是在关注单独的部分,但是stream专注于整体的画面,而并非单独的部分,下面我用两段代码来带你们理解下这里的意思。
public class Student {
//姓名
private String name;
//年龄
private Long age;
//性别
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getAge() {
return age;
}
public void setAge(Long age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
在上面我们创建了一个学生类,并且赋予了它三个属性,分别是姓名,年龄,性别。
三 关注单独部分
public static void main(String[] args) {
//添加学生信息
List <Student> list = new ArrayList<>();
list.add(new Student("鸡哥",20L,"女"));
list.add(new Student("坤哥",21L,"男"));
list.add(new Student("蔡哥",22L,"女"));
list.add(new Student("徐哥",23L,"男"));
//只查找一位学生中年龄大于22的(单独部分)
//用于装载年龄大于22的学生
List <Student> specialStu = new ArrayList<>();
int limit = 1;
int counter = 0;
for (Student stulist : list){
if (stulist.getAge() > 22){
specialStu.add(stulist);
counter ++;
if (counter == limit){
break;
}
}
}
specialStu.forEach(System.out::println);
}
在这段代码中,我们设置了个学生类型的集合,然后对该集合添加数据,在后面的语句我们又新建了一个specialStu用于装载年龄大于22的学生。
接下来看下面的循环部分,我们有Limit(限制器)限制我们只查询一条数据,Counter(计数器)每执行一次循环加一,在此处这个for循环里我们循环了全部学生信息,并在此筛选年龄大于22的学生信息,如果查询到有学生信息大于22,我们的计数器会加一,紧接着判断计数器是否与我们的限制条件相等,如果相等则打破整个循环,最后打印那位年龄大于22的学生信息,下面是控制台的信息。
⚪输出结果
student{name='徐哥',age=23,sex='男'}
可以看到,在这一段代码中我们更加注重在单个部分,也就是筛选出一个年纪为22的学生的信息,接下来我们在看看另一段代码。
四 关注整体部分
public class Test2 {
public static void main(String[] args) {
//添加学生信息
List<Student> list = new ArrayList<>();
list.add(new Student("鸡哥",20L,"女"));
list.add(new Student("坤哥",21L,"男"));
list.add(new Student("蔡哥",22L,"女"));
list.add(new Student("徐哥",23L,"男"));
List<Student> specialStu = list.stream()
.filter(stu -> stu.getAge() > 22)
.limit(1)
.collect(Collectors.toList());
specialStu.forEach(System.out::println);
}
}
⚪输出结果
student{name='徐哥',age=23,sex='男'}
上面的代码也是同样的输出结果,但是整体上效率更为高效也更为整洁,且整个流程执行下来是一个整体,并非像我们一开始的逻辑,把需要的数据筛选出来并传入最终的集合里,看起来十分的冗余,到这里我们的任务就已经完成了,可能目前你并不清楚它是怎么实现的,但是你不必担心,接下来我会为你解答如何使用Stream流。
五 Stream流两大类
在学会使用stream流之前我们先要知道
官方将stream流分为了两大类:终结操作(Terminaloperations)和中间操作(Intermediate operations)。
中间操作会返回一个新的流,一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后会返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。而是在终结操作开始的时候才真正开始执行。
终结操作是指返回最终的结果。一个流只能有一个终结操作,当这个操作执行后,这个流就被使用“光”了,无法再被操作。所以这必定这个流的最后一个操作。只有终结操作执行的时候,才会真正开始流的遍历,并且会生成一个结果。
六 中间操作
上图为中间操作所包含的部分方法,接下来回到之前的代码,在之前的代码中,我们使用了Filter,limit,这几种方法在我们的Stream流中都叫做中间操作,且有两个中间操作,每当我们使用了一次中间操作我们就会返回一个新的流交给后面的中间操作去处理。
七 终结操作
终结操作一般用于返回最终的结果,且一个流只能拥有一个终结操作,当这个终结操作执行后,这个流就被使用完了,无法继续操作。所以这必须是整个流的最后操作,只有当终结操作执行时才会开始真正的遍历,然后生成一个结果。
八 Stream流的使用
聊完Stream流的中间操作跟终结操作后接下来就该告诉大家Stream流的使用了。
8.1.Filter方法(过滤器:过滤出满足条件的元素)
在下面这段代码中,我们需要查找全部性别为女的学生的信息。
public static void main(String[] args) {
//添加学生信息
List<Student> list = new ArrayList<>();
list.add(new Student("鸡哥",20L,"女"));
list.add(new Student("坤哥",21L,"男"));
list.add(new Student("蔡哥",22L,"女"));
list.add(new Student("徐哥",23L,"男"));
//过滤出全部性别为女的学生信息
Stream<Student> specialStu = list.stream()
.filter(stu -> stu.getSex().equals("女"));
specialStu.forEach(System.out::println);
}
⚪输出结果
student{name='鸡哥',age=20,sex='女'}
student{name='蔡哥',age=22,sex='女'}
8.2.Limit方法(限制器:获取*个元素)
在下面这段代码中,我们需要获取前三名学生的信息。
public static void main(String[] args) {
//添加学生信息
List<Student> list = new ArrayList<>();
list.add(new Student("鸡哥",20L,"女"));
list.add(new Student("坤哥",21L,"男"));
list.add(new Student("蔡哥",22L,"女"));
list.add(new Student("徐哥",23L,"男"));
//取前三名学生的信息
Stream<Student> specialStu = list.stream()
.limit(3);
specialStu.forEach(System.out::println);
}
⚪输出结果
student{name='鸡哥',age=20,sex='女'}
student{name='坤哥',age=21,sex='男'}
student{name='蔡哥',age=22,sex='女'}
8.3.Count(计数器:用于计算集合的长度)
在下面这段代码中,我们需要查询出全部学生的个数。
public static void main(String[] args) {
//添加学生信息
List<Student> list = new ArrayList<>();
list.add(new Student("鸡哥",20L,"女"));
list.add(new Student("坤哥",21L,"男"));
list.add(new Student("蔡哥",22L,"女"));
list.add(new Student("徐哥",23L,"男"));
//取全部学生的数量
Long num = list.stream()
.count();
System.out.println("学生的个数为" + num);
}
⚪输出结果
学生的个数为4
8.4.Distinc(去重:用于去除集合里重复的元素)
在下面这段代码中,我们需要去除集合里重复的元素。
public static void main(String[] args) {
List <String> str = new ArrayList<>();
str.add(0,"h");
str.add(1,"e");
str.add(2,"l");
str.add(3,"l");
str.add(4,"o");
//去除集合重复的元素,并使用终结操作将其转化为List类型
List <String> distinct = str.stream()
.distinct()
.collect(Collectors.toList());
distinct.forEach(System.out::print);
}
⚪输出结果
helo
8.5.Sorted(排序:对数组里面的内容进行排序)
在下面这段代码中,我们要对集合进行排序。
public static void main(String[] args) {
List <Integer> str = new ArrayList<>();
str.add(0,1);
str.add(1,3);
str.add(2,5);
str.add(3,4);
str.add(4,2);
//对打乱的集合进行排序(从小到大),并使用终结操作将其转化为List集合
List <Integer> distinct = str.stream()
.sorted()
.collect(Collectors.toList());
distinct.forEach(System.out::print);
}
⚪输出结果
12345
8.6.Firstfind(查询第一个:查询出集合里第一个满足条件的元素)
在下面这段代码中,我们需要查询出第一个姓名为”蔡哥“的学生信息
public static void main(String[] args) {
//添加学生信息
List<Student> list = new ArrayList<>();
list.add(new Student("鸡哥",20L,"女"));
list.add(new Student("坤哥",21L,"男"));
list.add(new Student("蔡哥",22L,"女"));
list.add(new Student("徐哥",23L,"男"));
list.add(new Student("蔡哥",24L,"女"));
//查询出集合里第一个姓名为”蔡哥“的学生的信息
Optional<Student> first = list.stream()
.filter(stu -> stu.getName().equals("蔡哥"))
.findFirst();
System.out.println(first);
}
⚪输出结果
Optional[student{name='蔡哥',age=22,sex='女'}]
8.7.Map(映射:针对流中的每一个数据元素进行转换操作)
在下面这段代码中,我们需要将String类型的数组转化为Int类型的数组
public static void main(String[] args) {
//添加学生信息
List <String> str = new ArrayList<>();
str.add(0,"1");
str.add(1,"2");
str.add(2,"3");
str.add(3,"4");
//将String类型的集合转化为int类型,并使用终结操作返回List集合
List <Integer> in = str.stream()
.map(info -> {
return Integer.parseInt(info);
}).collect(Collectors.toList());
in.forEach(System.out::println);
}
⚪输出结果
1234
九 Stream性能
Stream流的处理速度是不如传统的Iterator(迭代器)处理速度快的,但是实际上在做业务的时候这些运行时间差都是可以忽略不计的,,恰恰相反Stream流可以使你的代码可读性及简洁度更高。
9.1Stream和迭代器的区别
Stream 可以并行化操作,迭代器只能命令式地、串行化操作。也就是当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程,下面用一张图来给你们解释下为什么Stream在并行时效率更高。
Iterator(迭代器)
迭代器在遍历时是读完当前元素后才会读取下一个元素。
Stream(流)
Stream流在并行的时候会将元素拆分成为多段来进行遍历,每个都在不同的线程中进行遍历,最后会将结果一起输出,相同的遍历在Stream并行的时候效率会高很多。
十 使用建议
当数据量不大或者没有太耗时的操作时,顺序执行(如iterator)往往比并行执行更快。当任务涉及到耗时操作(如I/O)并且任务之间不互相依赖时,那么并行化就是一个不错的选择。通常而言,将这类程序并行化之后,执行速度会提升好几个等级;