23、Stream流
1、简介
管道
管道就是一系列的聚合操作。【聚合,即指对数据的整理和总结,如最大值,最小值等】
管道包含以下组件:
(1)源:可以是集合,数组,生成器函数或I/O通道
(2)零个或多个中间操作:诸如过滤器之类的中间操作产生新的流
(3)终结操作:终端操作(例如forEach)会产生非流结果,例如原始值(如double),集合或者再在forEach的情况下根本没有任何值
流
流是一系列元素,与集合不同,它不是存储元素的数据结构。而是流通过管道携带来自源的值。
筛选器操作返回一个新的流,该流包含与筛选条件匹配的元素
2、如何获取流
(1)Collection
接口
default Stream<E> stream();
(2)Stream
接口
//获取流
public static<T> Stream<T> of(T....values);
//将两个流拼接成一个新的流
public static<T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b);
示例
public class StreamTest {
public static void main(String[] args) {
List<Integer> numbers1 = Arrays.asList(1, 2, 3, 4, 5);
//获取一个流
//集合接口
Stream<Integer> s1 = numbers1.stream();
//Stream接口
Stream<Integer> s2 = Stream.of(6, 7, 8, 9, 10);
//拼接
Stream<Integer> s3 = Stream.concat(s1, s2);
//Map接口
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
map.put("d", 4);
Stream<Map.Entry<String, Integer>> s4 = map.entrySet().stream();
}
}
3、Stream流中间聚合操作
Stream
接口
//根据给定的条件过滤流中的元素
Stream<T> filter(Predicate<? super T> predicate);
//将流中的元素进行类型转换
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//去重
Stream<T> distinct();
//排序,如果存储元素没有实现Comparable或者相关集合没有提供Comparator将抛出异常
Stream<T> sorted();
//根据给定的上限,获取流中的元素
Stream<T> limit(long maxSize);
//跳过给定数量的元素
Stream<T> skip(long n);
//将流中元素全部转为整数
InStream mapToInt(ToIntFunction<? super T> mapper);
//将流中元素全部转为长整数
LongStream mapToLong(ToLongFuncation<? super T> mapper);
//将流中元素全部转为双精度浮点数
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
示例
public class AggregateOperation {
public static void main(String[] args) {
//传统方法
List<Integer> nums = Arrays.asList(12, 3, 23, 54, 44);
for (Integer n : nums) {
if (n % 2 == 0) {
System.out.println(n);
}
}
//利用Stream流
Stream<Integer> s1 = Stream.of(12, 3, 23, 54, 44);
// Stream s2 = s1.filter(new Predicate<Integer>() {
// @Override
// public boolean test(Integer integer) {
// return integer % 2 == 0;
// }
// });
//Lambda表达式
Stream<Integer> s2 = s1.filter(integer -> integer % 2 == 0);
//打印元素
// s2.forEach(System.out::println);
//将流中的元素收集为一个List集合
List<Integer> existNumbers = s2.collect(Collectors.toList());
//将流中的元素收集为一个Set集合
Set<Integer> set = s2.collect(Collectors.toSet());
//将流中的元素收集为一个数组
// Integer[] arr = s2.toArray(new IntFunction<Integer[]>() {
// @Override
// public Integer[] apply(int value) {
// return new Integer[value];
// }
// });
Integer[] arr = s2.toArray(Integer[]::new);
}
}
4、Stream流终结操作
//遍历操作流中的元素
void forEach(Consumer<? super T> action);
//将流中元素按照给定转换方式转换为数组
<A> A[] toArray(IntFunction<A[]> generator);
//将流中的元素按照给定方式搜集起来
<R, A> R collect(Collector<? super T, A, R> collector);
//根据给定的排序方式获取流中的最小元素
Optional<T> min(Comparator<? super T> comparator);
//根据给定的排序方式获取流中的最大元素
Optional<T> max(Comparator<? super T> comparator);
//获取流中发第一个元素
Optional<T> findFirst();
//获取流中的元素个数
long count();
//检测流中是否存在给定条件的元素
boolean anyMatch(Predicate<? super T> predicate);
//检测流中是否全部满足给定条件
boolean allMatch(Predicate<? super T> predicate);
//检测流中元素是否全部不满足给定条件
boolean noneMatch(Predicate<? super T> predicate);
5、Stream流聚合操作与迭代器的区别
官方文档
聚合操作(如forEach)似乎像迭代器,但是,它们有几个本质区别:
(1)聚合操作使用内部迭代:聚合操作不包含诸如next的方法来指示它们处理集合的下一个元素。而是使用内部委托,你的程序确定要迭代的集合,而JDK确定如何迭代该集合。
而通过外部迭代,你的应用程序既可以确定要迭代的集合,又可以确定迭代的方式,但是外部迭代只能顺序地迭代集合的元素,内部迭代没有此限制。
内部迭代可以更轻松利用并行计算的优势,这涉及将问题分为子问题,同时解决这些问题,然后将解决方案的结果组合到子问题中。
(2)聚合操作从流中而不是直接从集合中处理元素,因此,它们也称为流操作
(3)它们支持将i行为作为参数,可以将lambda表达式指定为大多数聚合操作的参数,这使你可以自定义特定聚合