Stream流
流
概述
说道Stream流很容易想到I/O Stream,而实际上,Stream流不是I/O的一种,而是Lambda所带来的
的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库该有的弊端。
几乎所有的集合(Collection接口或Map接口等)都支持直接或者间接的遍历操作,而我们需要对集合中的元素进行操作的时候,除了必须的添加、删除、获取外,典型的就是集合遍历。
例如:
public class Demo01ForEach {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
for (String name : list) {
System.out.println(name);
}
}
}
这一段非常简单的集合遍历操作:对集合中的每一个字符串都激进型打印输出操作。
循环遍历的弊端
Java8中Lambda让我们可以更加专注于做什么(what),而不是怎么做(How)看上述代码,可以发现:
for循环的语法就是“怎么做”
for循环的循环体才是“做什么”
为什么使用循环?因为要进行遍历,但循环是遍历的唯一方式么?遍历是指每一个元素逐一进行处理,而并不是从第一个到最后一个顺次处理的循环。
试想一下,如果希望对集合中的元素进行筛选过滤:
- 将集合A根据条件一 过滤为子集B
- 然后再根据条件二 过滤子集C
那怎么办?在Java8之前的做法可能为:
//Java8 之前过滤操作
private static void test() {
List<String> list=new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
List<String> zhangList=new ArrayList<>();
for(String name:list){
if(name.startsWith("张")){
zhangList.add(name);
}
}
List<String> shortList=new ArrayList<>();
for(String name:zhangList){
if(name.length()==3){
zhangList.add(name);
}
}
for (String s : shortList) {
System.out.println(s);
}
}
这段代码中含有三个循环,每一个作用不同,
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么,并不是。
循环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。
那,Lambda的衍生物Stream能给我们带来怎么更加简洁的写法呢?
Stream更加简洁写法
先看一段代码:
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
list.stream()
.filter(s->s.startsWith("张"))
.filter(s->s.length()==3)
.forEach(System.out::println);
直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印。
流式思想概述
当需要对多个元素进行操作(特别是多步操作)的时候,考虑到性能及便利性,我们应该首先拼好一个模型步骤方案,然后再按照方案去执行。
想象一下图中展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种“函数模型”。每一个步骤都是一个流,调用指定的方法,可以从一个流模型转换为另一个流模型。
备注:“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)
Stream(流)是一个来自数据源的元素队列
数据源流的来源。可以是集合,数组等。
和以前的Collection操作不同,Stream操作还有两个基础的特征:
- Pipelining:中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格,这样做可以对操作进行优化,比如延迟执行和短路。
- 内部迭代:以前对集合遍历都是通过Iterator或者增强for的方式,显示的在集合外部进行迭代,这叫做外部迭代。Stream提供了内部迭代的方式,流可以直接调用遍历方法。
当使用一个流的时候,通常包括三个基本步骤:获取一个数据源 ->数据转换-> 执行操作获取想要的结果&