介绍
Stream流的出现,得益于 Lambda 多带来的 “函数式编程” , 用于解决已有集合类库的遍历的弊端
JDK8之前的集合遍历操作:
public class fun1 {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for(String item : list) {
System.out.println(name);
}
}
当我们需要对集合的元素进行操作的时候,总是需要循环,再循环。循环只是做业务的方式,而不是业务目的,Lambda 的衍生物 Stream 能给我们带来什么优雅的写法?
循环遍历的弊端:
JDK8 之前的 Lambda 专注于 做什么,而不是怎么做。但是 for 循环的语法关注于 怎么做,循环体才是做什么。所以有没有一种优雅写法可以把怎么做取代掉,专注于做什么?
Stream 优雅写法
借助 JDK8 的 Stream API , 关注于做什么,不关注怎么做
public class fun1 {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.stream()
.filter(name -> name.startWith('张')) // 过滤1
.filter(name -> name.length() == 3) // 过滤2
.forEach(name -> System.out.println(name)) // 输出
}
流式思想概述
当需要对多个元素进行操作,特别式多步操作的时候,考虑到性能和便利性,我们应当首先拼好一个函数模型步骤方案,然后按照方案去执行它
这张图展示了过滤,映射,跳过,技术等多步操作,这是一个集合元素的处理方案,而方案就是一种"函数模型" ,图的每一个方框就是一个 “流”,调用指定的方法,可以从一个流模型转到另一个流模型,而最右侧的数字3 就是最终结果
这里的 filter
,map
, skip
都是对函数模型进行操作,集合元素并没有真正的被处理,只有当终结方法 count
执行的时候,整个模型才会按照指定策略执行操作,这得益于 Lambda 的延迟执行特性
获取流
java.util.stream.Stream<T>
是 Java8 新增的最常用的流接口(并不是一个函数式接口)
获取流有两种方式:
- 方式一: 所有的
collection
集合都可以通过stream
默认方法获取流 - 方式二:
Stream
接口的静态方法of
可以获取数组对应的流
根据 Collection 获取流
由于,java.util.Collection
接口中加入了 default 方法 stream
用来获取流,所以其所有的实现类均可以获取流
//把集合转换为Stream流
List<String> list = new ArrayList<>();
Stream<String> streaml = list.stream(); .
Set<string> set = new HashSet<>();
Stream<String> stream2 = set.stream();
Map<String,String> map = new HashMap<>();
//获取键,存储到一个Set集合中
Set<String> keySet = map. keySet();
Stream<String> stream3 = keySet.stream();
//获取值,存储到一个Collection集合中
Collection<String> values = map.values();
Stream<String> stream4 = values.stream( );
//获取键值对<键与值的映射关系entrySet)
Set<Map.Entry<string, String>> entries = map. entrySet();
Stream<Map.Entry<string, String>> streams = entries.stream();
使用 of 获取流
//数组转为Stream流
Stream<Integer> stream6 = Stream.of(1,2,3,4,5);
// 可变参数可以传递数组
Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream7 = Stream.of(arr);
常用方法
流模型的操作很丰富,这里介绍-些常用的API。 这些方法可以被分成两种:
●延迟方法:返回值类型仍然是 stream 接口自身类型的方法,因此支持链式调用。( 除了终结方法外,其余方
法均为延迟方法。)
●终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 stringBuilder 那样的链
式调用。本小节中,终结方法包括 count 和 forEach 方法。
备注:本小节之外的更多方法,请自行参考API文档。
Stream 流属于 ”管道流“,只能被消费一次。意思是:第一个 Stream 流调用完毕后,数据就会流转到下一个 Stream 上,而这时第一个 Stream 流已经使用完毕,即关闭了,所以第一个Stream流就不能再调用方法了。
逐一处理:forEach
作用:用于遍历流中的数据,由于属于终结方法,遍历之后就不能继续调用 Stream 流的其他方法
// 获取一个 Stream 流
Stream<String> stream7 = Stream.of("a","b","c");
// 对 Stream 流中的数据进行遍历
stream7.forEach( (name) -> {
System.out.println(name);
});
过滤:filter
作用:通过 filter
方法将流转为另一个子集流
// 获取一个 Stream 流
Stream<String> stream1 = Stream.of("a","b","c");
// 对 stream 流进行过滤
Stream<String> stream2 = stream1.filter(item -> {
return item.startsWith('7');
});
stream2.forEach(item -> System.out.println(item));
映射:map
作用:将流中的元素映射到另一个流中
// 获取一个 Stream 流
Stream<String> stream1 = Stream.of("1","2","3");
// 使用 map 方法,对流进行映射
Stream<Integer> stream2 = stream1.map(s -> {
return Integer.parseInt(s);
});
stream2.forEach(item -> System.out.println(item));
个数:count
作用:用于统计 Stream 流中元素的个数
返回值:long 类型的整数
// 获取一个 Stream 流
Stream<String> stream1 = Stream.of("1","2","3");
// 使用 map 方法,对流进行映射
long count = stream1.count();
截取:limit
作用:对流进行截取,只取前 n 个
参数:long 类型
// 获取一个 Stream 流
Stream<String> stream1 = Stream.of("1","2","3");
// 使用 limit 方法,对流进行截取
Stream<String> result = stream1.limit(2);
跳过:skip
作用:用于跳过前几个
参数:long 型
// 获取一个 Stream 流
Stream<String> stream1 = Stream.of("1","2","3");
// 使用 limit 方法,对流进行截取
Stream<String> result = stream1.skip(2);
拼接:contact
作用:Stream 流的静态方法,用于将两个流合并为一个流
// 获取一个 Stream 流
Stream<String> stream1 = Stream.of("1","2","3");
// 获取一个 Stream 流
Stream<String> stream2 = Stream.of("1","2","3");
Stream<String> result = Stream.contach(stream1,stream2)