Stream
Stream是Java 8中处理集合的关键抽象概念,可以执行非常复杂的查找、过滤、和映射数据操作,使用Stream API对集合数据进行操作,就类似使用SQL执行的数据库查询。Stream不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。Iterator只能显式地一个一个去遍历元素并对其执行某些操作,而使用Stream,只要给出需要对其包含的元素执行什么操作,Stream会隐式地在内部进行遍历,做出相应的数据转换。和迭代器又不同的是,Stream可以并行化操作,迭代器只能命令式地、串行化操作。
Stream和Collection的区别主要有
- stream本身并不存储数据,数据是存储在对应的collection里,或者在需要的时候才生成的
- stream不会修改数据源,总是返回一个持有结果的新的stream
- stream的操作是懒执行的:仅当最终的结果需要的时候才会执行,比如上面的例子中,结果仅需要前3个长度大于7的字符串,那么在找到前3个长度符合要求的字符串后,filter()将停止执行
Stream操作步骤
- 创建Stream
- 中间操作
- 终止操作
创建Stream
//1.通过Collection集合提供的stream() 或parallelStream
List<String> list= new ArrayList<>();
list.stream()
//2.通过Arrays的静态方法,传入一个泛型数组,创建一个流
String [] streamArr= new String []{"1","2","3","4"};
Arrays.stream(streamArr);
//3.通过Stream的静态方法,传入一个泛型数组,或者多个参数,创建一个流,他的底层也是使用的Arrays的stream方法
Stream.of(streamArr);
//4.创建无限流
//迭代
Stream.iterate()
//生成
Stream.generate()
中间操作
中间操作的返回值都是一个新流。多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,这种处理被称为惰性求值
筛选与切片
- filter
接收一个Predicate,从流中排除某些元素,返回满足条件的流 - limit
截断,使元素不超过给定数量 - skip
跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则返回一个空流 - distinct
筛选,通过流所生成元素的hashCode和equals去去除重复元素,需要重写这两个方法
映射
- map
接受一个函数型接口作为参数,将元素转换成其他形式,或用于提取信息 - flatMap
接受一个函数型接口作为参数,将流中的每个值都转换成另一个流,然后把所有的流连接成一个流,这个方法和map的区别是,会将所有的映射元素封装成一个对象
排序
- sorted
产生一个新流,其中按自然顺序排序,也可以传入一个Comparator接口,按照比较器排序
终止操作
查找与匹配
- allMatch
检查是否匹配所有元素,全都满足才返回true,流为空时总是返回true - anyMatch
检查是否至少匹配一个元素,有一个满足条件就返回true - noneMatch
检查是否没有匹配所有的元素,全都不满足返回true - findeFirst
返回第一个元素(可以配合排序使用) - findAnt
返回当前流中的任意元素(可以用来返回某种状态的任意一个对象,配合并行流使用) - count
返回当前流中的元素的总个数 - max
返回流中的最大值(排序后的第一个) - min
返回流中的最小值(排序后的最后一个) - forEach
内部迭代
归约
- reduce
reduce操作将流中的元素根据指定的计算模型,反复结合起来,得到一个值。之前提到count、sum、min和max方法,因为常用而被纳入标准库中,其实这些都是reduce操作
//reduce方法有三个override的方法
//对Stream中的数据通过累加器accumulator迭代计算,最终得到一个Optional对象
Optional<T> reduce(BinaryOperator<T> accumulator);
//给定一个初始值identity,通过累加器accumulator迭代计算,得到一个同Stream中数据同类型的结果
T reduce(T identity, BinaryOperator<T> accumulator);
//给定一个初始值identity,通过累加器accumulator迭代计算,得到一个identity类型的结果,第三个参数用于使用并行流时合并结果
U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner);
函数式接口BinaryOperator,该函数式接口需要两个参数,返回一个结果(reduce中返回的结果会作为下次累加器计算的第一个参数),也就是所讲的累加器。如果不指定初始值,使用第一个方法,Stream中的第一个元素会作为初始值。
收集
- collect
将流转化为其他形式,接收一个Collector接口的实现,用于给Stream中的元素做汇总
Collectors实用类提供了很多的静态方法,可以方便的创建常见收集器的实例
Collectors类常用方法
toList
toSet
toCollection
counting
summingInt
averagingInt
汇总 summarizingInt
IntSummaryStatistics summaryStatistics =personList.stream()
.collect(Collectors.summarizingInt(PersonBean::getSalary));
summaryStatistics.getCount();
summaryStatistics.getMax();
summaryStatistics.getMin();
summaryStatistics.getSum()
字符串连接 joining
maxBy
minBy
分组 groupingBy
// 根据年龄分组
Map<String, List<PersonBean>>personMapByAge =
personList.stream()
.collect(Collectors.groupingBy(PersonBean::getUserAge));
// 分组,并统计每个组的个数
Map<String, Long> personMapByAgeNum =
personList.stream()
.collect(Collectors
.groupingBy(PersonBean::getUserAge, Collectors.counting()));
// 根据年龄分组,并统计每个组的工资和
Map<String, Integer>personMapBySalarySum =
personList.stream()
.collect(Collectors
.groupingBy(PersonBean::getUserAge,Collectors
.summingInt(PersonBean::getSalary)));
// 根据年龄和性别分组,多分组的话Map可无限嵌套
Map<String, Map<String, List<PersonBean>>>personMapByAgeAndSex
= personList.stream()
.collect(Collectors
.groupingBy(PersonBean::getUserAge, Collectors
.groupingBy(PersonBean::getUserSex)));
分区 partitioningBy
Collector<T, ?, Map<Boolean, List<T>>>
partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
组合
- concat
用来连接类型一样的两个流