Java 8 新特性 Stream流操作
我的理解
- 在Java8中API新增了一个新的抽象称为流Stream,可以让我们通过声明的方式处理流Stream。
- 我个人的理解是Stream流类似于SQL语句一样从数据库通过查询数据这种方式,将Java集合和表达进行抽象,以一种直观的方式展示出来。
- 这个API通过干净、简洁以及高效率的代码提高Java程序员对数据的处理效率。
什么是Stream
通过这个单词的字面意思,可以知道它是流的意思,它是一个来自数据源的元素队列,且它支持类似SQL语句的聚合操作
- 元素 是某种类型的对象,形成一个队列,比如
String
类型的队列List<String>
,此时的元素就是String
类型的元素,Java中的Stream不会对元素进行修改和存储,而是根据你的需求进行计算,如过滤,映射,去除,查找,匹配,排序。 - 数据源 它可以使集合、数组等,它是流的来源。
- 聚合操作 如上述所说的过滤,映射,去除,查找,匹配,排序等操作,类似SQL语句。
- 流水线(Pipelining) 所有中间操作都会回到流本身,打个比方,流水线中一批产品需要通过不同的检查筛选,第一道操作之后剩下的产品进入第二道操作,之后最后的操作执行后剩下的产品就是想要的优质品。将多个操作串联成一个管道,类似fluent style。这样的话好处是可以对操作进行优化。
- 内部迭代 没有Stream流之前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 而Stream提供了内部迭代的方式, 通过访问者模式实现。
如何生成流
在Java8中,有两个方法对集合接口进行生成流:
Stream()
创建串行流parallelStream()
创建并行流
filter
filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤空字符串:
List<String> str = Arrays.asList("S","t","","r","e","a","m");
System.out.println("未通过Stream流操作的str:" + str);
List<String> filter = str.stream().filter(s->!s.isEmpty()).collect(Collectors.toList());
System.out.println("通过Stream流操作的str:" + str);
System.out.println("通过Stream流操作返回的filter:" + filter);
该操作将str
中的空字符去除掉后将剩余的元素返回给filter
中,并不会对原本的str
进行操作。
输出结果
未通过Stream流操作的str:[S, t, , r, e, a, m]
通过Stream流操作的str:[S, t, , r, e, a, m]
通过Stream流操作返回的filter:[S, t, r, e, a, m]
进程已结束,退出代码为 0
forEach
在Stream中提供了forEach
来迭代每个数据,以下代码使用forEach输出了从0到9中的10个随机数
Random random = new Random();
random.ints(0,10).limit(10).forEach(System.out::println);
定义一个Random
对象,调用ints().limit(10)
随机获取10个整数后进行输出。
输出结果
9
0
6
0
8
3
7
9
6
0
进程已结束,退出代码为 0
map
map 方法用于映射每个元素到对应的结果,以下代码使用map输出元素对应的平方数:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数并将重复的数据去除
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.println(squaresList);
通过map(i -> i*i )
得到元素对应的平方数,并通过distinct()
将重复的数据去除,最后通过collect()
将数据返回。
输出结果:
[9, 4, 49, 25]
进程已结束,退出代码为 0
limit
limit 方法用于获取指定数量的流。 引用上面forEach代码片段使用 limit 方法打印出 10 条数据:
Random random = new Random();
random.ints(0,10).limit(10).forEach(System.out::println);
定义一个Random
对象,调用ints().limit(10)
随机获取10个整数后进行输出。
输出结果
9
0
6
0
8
3
7
9
6
0
进程已结束,退出代码为 0
sorted
sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的数字进行排序:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().sorted().forEach(System.out::println);
通过调用stream().sorted()
将numbers中的数据进行排序
输出结果
2
2
3
3
3
5
7
进程已结束,退出代码为 0
并行(parallel)程序
parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出去除空字符串后的结果:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("未通过Stream流操作的str:" + strings);
// 获取空字符串的数量
List<String> filter = strings.parallelStream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("通过Stream流操作的str:" + strings);
System.out.println("通过Stream流操作返回的filter:" + filter);
输出结果:
未通过Stream流操作的strings:[abc, , bc, efg, abcd, , jkl]
通过Stream流操作的strings:[abc, , bc, efg, abcd, , jkl]
通过Stream流操作返回的filter:[abc, bc, efg, abcd, jkl]
进程已结束,退出代码为 0
Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
List<String>strings = Arrays.asList("S", "", "t", "r", "e","a", "m","");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining());
System.out.println("合并字符串: " + mergedString);
先通过filter
将空字符串去除,然后使用Collectors
中的joining()
将其合并成一个字符串。
筛选列表: [S, t, r, e, a, m]
合并字符串: Stream
进程已结束,退出代码为 0
统计
用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
输出结果:
列表中最大的数 : 7
列表中最小的数 : 2
所有数之和 : 25
平均数 : 3.5714285714285716
进程已结束,退出代码为 0
以上例子参考菜鸟教程Java 8 Stream | 菜鸟教程加以修改
实际例子
统计学生的成绩,筛选出成绩大于60的学生,输出其名字和成绩(这里只是做例子所以名字直接使用数字代替,成绩使用random生成)
Random random = new Random();
List<Student> StuList = new ArrayList<Student>();
for(int i=0;i<20;i++) {
Student s = new Student(String.valueOf(i),random.nextDouble()*100);
StuList.add(s);
}
System.out.println("所有成绩:" + StuList.toString());
List<Student> collect = StuList.stream().filter(s -> s.grade > 60).collect(Collectors.toList());
System.out.println("筛选后的成绩:" + collect.toString());
先定义一个Random
对象,用于生成成绩,通过循环创建20个Student
类型的对象,名字使用循环的变量i进行代替,成绩通过random.nextDouble()
进行生成,通过add()
方法将对象添加进列表中,然后使用stream().filter()
进行筛选出成绩大于60的学生并输出
所有成绩:[0 61.0, 1 59.4, 2 19.1, 3 2.2, 4 78.8, 5 69.7, 6 98.8, 7 3.1, 8 77.7, 9 35.9, 10 60.8, 11 95.4, 12 66.4, 13 76.9, 14 51.8, 15 87.0, 16 39.0, 17 80.7, 18 79.4, 19 81.0]
筛选后的成绩:[0 61.0, 4 78.8, 5 69.7, 6 98.8, 8 77.7, 10 60.8, 11 95.4, 12 66.4, 13 76.9, 15 87.0, 17 80.7, 18 79.4, 19 81.0]
进程已结束,退出代码为 0
总结
关于Stream流的内容就讲到这里了,通过Stream流我们可以写出更简洁高效的代码对这些数据进行处理,如过滤,映射,去除,查找,匹配,排序,不只是单一的操作,而是可以多个操作进行,得到我们所需要的数据,且不会对原数据进行操作,所以Stream流还是需要好好掌握的。