所有示例代码打包下载 : 点击打开链接
Java8新特性 :
Stream流是Java8 API的新成员 , 它允许你以声明的方式处理数据集合(通过类似SQL的查询语句来表达 , 而不是临时编写一个实现) . 就现在来说 , 你可以把它们看成遍历数据集的高级迭代器 , 熟练应用之后 , 在大部分情况下你都不需要再写循环去遍历集合了 . 此外 , 流还可以透明的并行处理 , 也不需要写任何多线程代码了 . 这是目前为止最大的一次对Java库的完善 , 颠覆了Java8之前的代码书写风格 . 例如 , 对集合中的每个元素进行运算并转换为另一个集合
List oldList = Arrays.asList(1 , 2 , 3 , 4 , 5);
List newList1 = new ArrayList<>();
//Java8之前
for(Integer i : oldList){
newList1.add(i * 100 + "分");
}
//Java8
List newList2 = oldList.stream().map(i -> i * 100 + "分").collect(Collectors.toList());
}
从上述代码来看 , 你可能看不出明显的改变(只不过从3行代码优化成一行而已) , 但随着了解 , 你一定会惊叹Stream强大的功能 .
Stream的风格就是将要处理的元素集合看成一种流 , 流在管道中传输 , 并且在管道中不同的节点上进行处理 , 比如筛选 , 排序 , 聚合等 , 然后输出最终符合我们需求的集合数据 . 即元素流在管道中经过中间操作(intermediate operation)的处理 , 最后由最终操作(terminal operation)得到前面处理的结果 . 由于Stream过于强大 , 我们这里只将常用的做一下说明
在Java8中 , 集合接口有两个方法来生成流 :
Stream stream1 = list1.stream();//Stream stream();为集合创建串行流
Stream stream2 = list1.parallelStream();//Stream parallelStream();为集合创建并行流 , 在使用过程中 ,你也随时可以调用parallel()方法将一个串行流转为并行流
同时 , Stream类有几个工厂方法可以创建Stream :
Stream
类别 | 方法 | 描述 | 备注 |
遍历 | void forEach(Consumer super T> action) | 遍历操作,遍历执行Consumer 对应的操作 | |
筛选/切片 | Stream |
过滤操作,根据Predicate判断结果保留为真的数据,返回结果仍然是流 | |
Stream |
去重操作,筛选出不重复的结果,返回结果仍然是流 | ||
Stream |
截取限制操作,只取前 maxSize条数据,返回结果仍然是流 | ||
Stream |
跳过操作,跳过n条数据,取后面的数据,返回结果仍然是流 | ||
排序 | Stream |
对流中的元素进行排序 , 返回结果仍然是流 | |
映射 | Stream |
转化操作,根据参数T,转化成R类型,返回结果仍然是流 | |
Stream |
转化操作,根据参数T,转化成R类型流,这里会生成多个R类型流,返回结果仍然是流 | ||
匹配 | boolean anyMatch(Predicate super T> predicate) | 判断是否有一条匹配,根据Predicate判断结果中是否有一条匹配成功 | |
boolean allMatch(Predicate super T> predicate) | 判断是否全都匹配,根据Predicate判断结果中是否全部匹配成功 | ||
boolean noneMatch(Predicate super T> predicate) | 判断是否一条都不匹配,根据Predicate判断结果中是否所有的都不匹配 | ||
查找 | Optional |
查找操作, 查询当前流中的任意元素并返回Optional | |
Optional |
查找操作, 查询当前流中的第一个元素并返回Optional | ||
归约 | T reduce(T identity, BinaryOperator |
归约操作,同样两个类型的数据进行操作后返回相同类型的结果。比如两个整数相加、相乘等。 | |
Optional |
求最大值,根据Comparator计算的比较结果得到最大值 | ||
Optional |
求最小值,根据Comparator计算的比较结果得到最小值 | ||
汇总统计 | 汇总操作,汇总对应的处理结果。 | ||
long count() | 统计流中数据数量 | ||
最终操作 | R collect(Collector super T, A, R> collector) | 通常出现在管道传输操作结束标记流的结束 |
Stream.of(1,2,3,1,2,3).distinct().forEach(System.out::println); // 打印结果:1,2,3
创建了一个Stream(命名为A),其含有重复的1,2,3等六个元素,而实际上打印结果只有“1,2,3”等3个元素。因为A经过distinct去掉了重复的元素,生成了新的Stream(命名为B),而B中只有“1,2,3”这三个元素,所以也就呈现了刚才所说的打印结果。
Stream.of(1, 2, 3, 4, 5).filter(item -> item > 3).forEach(System.out::println);// 打印结果:4,5
创建了一个含有1,2,3,4,5等5个整型元素的Stream,filter中设定的过滤条件为元素值大于3,否则将其过滤。而实际的结果为4,5。
Stream.of("a", "b", "hello").map(item-> item.toUpperCase()).forEach(System.out::println);// 打印结果 A, B , HELLO
传给map中Lambda表达式,接受了String类型的参数,返回值也是String类型,在转换行数中,将字母全部改为大写
flatMap:和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中;
Stream.of(1, 2, 3, 4, 5)
.peek(integer -> System.out.println("accept:" + integer))
.forEach(System.out::println);
// 打印结果
// accept:1
// 1
// accept:2
// 2
// accept:3
// 3
// accept:4
// 4
// accept:5
// 5
peek方法生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数,并且消费函数优先执行
Stream.of(1, 2, 3,4,5).skip(2) .forEach(System.out::println); // 打印结果 3,4,5
Stream.of(1, 2, 3,4,5).limit(2).forEach(System.out::println);// 打印结果 1,2
传入limit的值为2,也就是说被截取后的Stream的最大长度为2,又由于原Stream中有5个元素,所以将截取原Stream中的前2个元素,生成一个新的Stream。
其他常用的api还有toSet , toCollection , toMap等 , 关于更高级的用法 , 可以参看这篇博文 : http://blog.csdn.net/IO_Field/article/details/54971608
Collection接口的实现类调用parallelStream方法就可以实现并行流,相应地也获得了并行计算的能力。或者Stream接口的实现调用parallel方法也可以得到并行流。并行流实现机制是基于fork/join 框架,将问题分解再合并处理。
不过并行计算是否一定比串行快呢?这也不一定。实际影响性能的点包括: