JDK新特性(五)——Stream流
前言
Stream流的概念是在JDK1.8的时候提出来的,是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。通常我们需要多行代码才能完成的操作,借助于Stream流式处理可以很简单的实现。本文将对Stream流的工作流程和常用的api进行讲解,希望对各位读者有所帮助。
提示:如果你对Lambda表达式、函数式接口和方法引用等概念没有了解,建议先去阅读本系列文章中这部分的内容,再来阅读本文:
JDK新特性(一)——Lambda表达式
JDK新特性(三)——方法引用
JDK新特性(四)——函数式接口
一、Stream流的初使用
我们在前言中讲到,Stream
流可以实现对集合对象处理步骤的简化和大批量数据的操作,相比于本系列其他文章而言,Stream
流可能是我们工作中相对来说使用和接触得更多的特性了。
下面我们不妨来看一个例子,体验一下Stream流的强大之处:
要求我们定义一个方法,把字符串数组中以张开头,字符串长度大于2的字符进行输出。
public class StreamTest {
public static void main(String[] args) {
String[] arr = {"张三丰","张超","章口就来","张口就来"};
filterStrMethod(arr);
}
private static void filterStrMethod(String[] arr){
List<String> nameList = new ArrayList<>();
List<String> lengthList = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
if(arr[i].startsWith("张")){
nameList.add(arr[i]);
}
}
for (int i = 0; i < nameList.size(); i++) {
if(nameList.get(i).length()>2){
lengthList.add(nameList.get(i));
}
}
System.out.println(lengthList);
}
}
(上面这个方法自然可以只用一个for
循环解决,目的主要是出于模拟过滤两次的效果)
我们可以看到,使用常规的for循环虽然可以解决问题,但相对来说代码量不少。我们再来看看使用Stream流可以怎么样来优化解决的方案:
public class StreamTest {
public static void main(String[] args) {
String[] arr = {"张三丰","张超","章口就来","张口就来"};
filterStrByStream(arr);
}
private static void filterStrByStream(String[] arr){
Stream.of(arr).filter(s -> s.startsWith("张")).filter(s -> s.length()>2).forEach(System.out::println);
}
}
我们可以看到,使用Stram流的方式后,原先的需求我们只用了一行代码就实现了!!!
这就是Stream
流的强大之处,让我们的代码更简洁,更简单直接。Stream
流把真正的函数式编程风格引入到Java中
二、Stream流的生成方式
我们对流的操作一般来说会从三个阶段进行,分别是流的生成、流的中间操作和流的终结操作方法。我们将源数据转换成流对象后,可以对其进行过滤、排序、元素跳跃和元素限制等操作,最终再使用例如foreach
的方法对流进行结束。
三、Stream流操作篇
(一)Stream流的生成方式
Stream流支持多种渠道的数据源转为流对象,下面将主要进行Collection
等集合类的流生成方式
Collection体系集合
使用默认方法stream()生成流, default Stream stream()
Map体系集合
把Map转成Set集合,间接的生成流
数组
通过Stream接口的静态方法of(T... values)生成流
下面我们来用代码演示一下流的生成
public class StreamCreateDemo {
public static void main(String[] args) {
streamCreateTest();
}
private static void streamCreateTest(){
// List集合转Stream流
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
// Set集合转Stream流
Set<Integer> set = new HashSet<>();
Stream<Integer> setStream = set.stream();
// Map集合间接转Stream流
Map<String,Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
// 数组转Stream流,使用 Stream接口中的静态方法 of(T ...values)
String[] arr = new String[5];
Stream<String> arrStream1 = Stream.of(arr);
Stream<String> arrStream2 = Stream.of("10","20","13");
}
}
(二)Stream流的中间操作方法
中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作.
方法名 | 说明 |
---|---|
Stream filter(Predicate predicate) | 用于对流中的数据进行过滤 |
Stream limit(long maxSize) | 返回此流中的元素组成的流,截取前指定参数个数的数据 |
Stream skip(long n) | 跳过指定参数个数的数据,返回由该流的剩余元素组成的流 |
static Stream concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
Stream distinct() | 返回由该流的不同元素(根据Object.equals(Object))组成的流 |
Stream sorted() | 返回由此流的元素组成的流,根据自然顺序排序 |
Stream sorted(Comparator comparator) | 返回由该流的元素组成的流,根据提供的Comparator进行排序 |
Stream map(Function mapper) | 返回由给定函数应用于此流的元素的结果组成的流 |
lntStream mapTolnt(TolntFunction mapper) | 返回一个Intstream其中包含将给定函数应用于此流的元素的结果 |
下面我们就来演示一下吧
filter方法
要求过滤出Stream
流中以胡开头,字符长度大于2的元素
public class StreamCreateDemo {
public static void main(String[] args) {
List list = Arrays.asList("胡宗宪","胡萝卜","胡说","大胡子");
filterTest(list);
}
private static void filterTest(List<String> list){
list.stream().filter(s->s.startsWith("胡")&&s.length()>2).forEach(System.out::println);
}
}
limit和skip方法
要求跳过Stream
流中中前两个元素,只取接下来后面的两个元素
public class StreamCreateDemo {
public static void main(String[] args) {
List list = Arrays.asList("胡宗宪","刘亦菲","流光溢彩","胡萝卜","胡说","大胡子");
skipAndLimitTest(list);
}
private static void skipAndLimitTest(List<String> list){
list.stream().skip(2).limit(2).forEach(s -> System.out.println(s));
}
}
concat和distinct方法
要求将两个流进行合并,然后去重后进行打印输出
public class StreamCreateDemo {
public static void main(String[] args) {
List list1 = Arrays.asList(10,20,30,40);
List list2 = Arrays.asList(30,40,50,60);
concatAndDistinctTest(list1,list2);
}
private static void concatAndDistinctTest(List<Integer> list1,List<Integer> list2){
Stream listStream1 = list1.stream();
Stream listStream2 = list2.stream();
Stream.concat(listStream1,listStream2).distinct().forEach(System.out::println);
}
}
sort方法
要求:将Stream
流中的元素根据字符长度进行(正序)排序,如果长度相等,则按照字母大小排序。
public class StreamCreateDemo {
public static void main(String[] args) {
List list1 = Arrays.asList("张国荣","刘翔","黄志明","于谦");
sortTest(list1);
}
private static void sortTest(List<String> list){
// comparingInt表示将比较括号内的两个元素的字符长度
list.stream().sorted(Comparator.comparingInt(String::length).thenComparing((s1,s2) -> (s1.compareTo(s2)>0?-1:1))).forEach(System.out::println);
}
map和mapToInt方法
map
和mapInt
方法和上面的其他方法略有不同,因为其他方法只是对流中的元素进行过滤、排序等操作,并不会真正改动到流中的元素内容,而map
就可以实现改变流中元素
要求将Stream
流中的字符串元素转为整形元素进行打印输出
public class StreamCreateDemo {
public static void main(String[] args) {
List list1 = Arrays.asList("124","13","134","163");
mapTest(list1);
}
private static void mapTest(List<String> list){
list.stream().map(Integer::parseInt).forEach(System.out::println);
}
}
要求将Stream
流中的字符串元素转为整形元素后进行求和操作
public class StreamCreateDemo {
public static void main(String[] args) {
List list1 = Arrays.asList("124","13","134","163");
mapIntTest(list1);
}
private static void mapIntTest(List<String> list){
System.out.println(list.stream().mapToInt(Integer::parseInt).sum());
}
}
(三)Stream流的终止操作
终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作。
常用方法
方法名 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行操作 |
long count() | 返回此流中的元素数 |
下面就用一个小案例来演示一下吧
public class StreamCreateDemo {
public static void main(String[] args) {
List list1 = Arrays.asList("韦小宝","郑成功","王多鱼","刘翔");
countTest(list1);
}
private static void countTest(List<String> list){
long count = list.stream().filter(s -> s.length() > 2).count();
System.out.println(count);
}
}
四、Stream流的收集操作
流的收集操作是指:我们对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中。
常用方法
方法名 | 说明 |
---|---|
R collect(Collector collector) | 把结果收集到集合中 |
我们可以看到,collect
方法需要我们传递一个Collector
对象,但Collector
是一个接口,所以我们一般会借助工具类Collectors
来帮助我们实现收集的动作。
工具类Collectors
提供了具体的收集方式
方法名 | 说明 |
---|---|
public static Collector toList() | 把元素收集到List集合中 |
public static collector toSet() | 把元素收集到set集合中 |
public static Collector toMap(Function keyMapper,Function valueMapper) | 把元素收集到Map集合中 |
下面我们就用一个小案例来演示一下如何使用Stream流的收集操作吧
public class StreamCreateDemo {
public static void main(String[] args) {
List list1 = Arrays.asList("韦小宝","郑成功","王多鱼","刘翔","韦小宝");
List list2 = Arrays.asList("韦小宝,12","郑成功,41","王多鱼,12","刘翔,45","大宝,41");
collectToSet(list1);
collectToList(list1);
collectToMap(list2);
}
private static void collectToSet(List<String> list){
Set<String> set = list.stream().filter(s -> s.length() > 2).collect(Collectors.toSet());
System.out.println(set);
}
private static void collectToList(List<String> list){
List<String> result = list.stream().filter(s -> s.length() > 2).collect(Collectors.toList());
System.out.println(result);
}
private static void collectToMap(List<String> list){
Map<String,Integer> map = list.stream().filter(s -> s.length() > 2).collect(Collectors.toMap(s -> s.split(",")[0],s -> Integer.parseInt(s.split(",")[1])));
for(String key : map.keySet()){
Integer num = map.get(key);
System.out.println(key + " : " + num);
}
}
}
五、注意事项
1. 流不能被重复使用,流关闭后也不能再被使用
2. 流的每个中间操作都会返回一个全新的流对象,直到遇到及早求值(终止操作)才会执行得到最终结果,如果没有终止操作,则所有中间操作根本不会执行。
至此有关Stream
流的介绍就到这里了,Stream流的使用可以在一定程度上减少减少我们在工作中的代码开发量,学习的成本相当来说也不高,个人觉得Stream
流的应用属于java开发人员应该要掌握的知识点之一。
参考文章:
Stream流介绍及实例:
https://www.jianshu.com/p/43f853c3c5d3