从零了解Stream
Stream
Stream的操作符大体上分为两种:中间操作符和终止操作符
中间操作符
对于数据流来说,中间操作符在执行制定处理程序后,数据流依然可以传递给下一级的操作符。
中间操作符包含8种(排除了parallel,sequential,这两个操作并不涉及到对数据流的加工操作):
- map(mapToInt,mapToLong,mapToDouble) 转换操作符,把比如A->B,这里默认提供了转int,long,double的操作符。
- flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 变成 2,3,4 也就是从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作符。
- limit 限流操作,比如数据流中有10个 我只要出前3个就可以使用。
- distint 去重操作,对重复元素去重,底层使用了equals方法。
- filter 过滤操作,把不想要的数据过滤。
- peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等。
- skip 跳过操作,跳过某些元素。
- sorted(unordered) 排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。
终止操作符
数据经过中间加工操作,就轮到终止操作符上场了;终止操作符就是用来对数据进行收集或者消费的,数据到了终止操作这里就不会向下流动了,终止操作符只能使用一次。
- collect 收集操作,将所有数据收集起来,这个操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以说Stream 的核心在于Collectors。
- count 统计操作,统计最终的数据个数。
- findFirst、findAny 查找操作,查找第一个、查找任何一个 返回的类型为Optional。
- noneMatch、allMatch、anyMatch 匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值。
- min、max 最值操作,需要自定义比较器,返回数据流中最大最小的值。
- reduce 规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
- forEach、forEachOrdered 遍历操作,这里就是对最终的数据进行消费了。
- toArray 数组操作,将数据流的元素转换成数组。
函数式接口
函数式接口定义规则
- 有且仅有一个抽象函数
- 必须要有@FunctionalInterface 注解
- 可以有默认方法
Java8 util.function 包下自带了43个函数式接口,大体分为以下几类:
- Consumer 消费接口
- Function 功能接口
- Operator 操作接口
- Predicate 断言接口
- Supplier 生产接口
Consumer
消费者接口,就是用来消费数据的。
Consumer 接口中有accept 抽象方法,accept接受一个变量,也就是说你在使用这个函数式接口的时候,给你提供了数据,你只要接收使用就可以了;andThen 是一个默认方法,接受一个Consumer 类型,当你对一个数据使用一次还不够爽的时候,你还能再使用一次,当然你其实可以爽无数次,只要一直使用andThan方法。
Function
何为Function呢?比如电视机,给你带来精神上的愉悦,但是它需要用电啊,电视它把电转换成了荷尔蒙,这就是Function,简单电说,Function 提供一种转换功能。
Function 接口 最主要的就是apply 函数,apply 接受T类型数据并返回R类型数据,就是将T类型的数据转换成R类型的数据,它还提供了compose、andThen、identity 三个默认方法,compose 接受一个Function,andThen也同样接受一个Function,这里的andThen 与Consumer 的andThen 类似,在apply之后在apply一遍,compose 则与之相反,在apply之前先apply(这两个apply具体处理内容一般是不同的),identity 起到了类似海关的作用,外国人想要运货进来,总得交点税吧,然后货物才能安全进入中国市场,当然了想不想收税还是你说了算的。
Operator
可以简单理解成算术中的各种运算操作,当然不仅仅是运算这么简单,因为它只定义了运算这个定义,但至于运算成什么样你说了算。由于没有最基础的Operator,这里将通过 BinaryOperator、IntBinaryOperator来理解Operator 函数式接口,先从简单的IntBinaryOperator开始。
IntBinaryOperator
从名字可以知道,这是一个二元操作,并且是Int 类型的二元操作,那么这个接口可以做什么呢,除了加减乘除,还可以可以实现平方(两个相同int 数操作起来不就是平方吗)。
BinaryOperator
BinaryOperator 二元操作,看起来它和IntBinaryOperator 是父子关系,实际上这两者没有半点关系,但他们在功能上还是有相似之处的
BinaryOperator 是 BiFunction 生的,而IntBinaryOperator 是从石头里蹦出来的,BinaryOperator 自身定义了minBy、maxBy默认方法,并且参数都是Comparator,就是根据传入的比较器的比较规则找出最小最大的数据。
Predicate
断言、判断,对输入的数据根据某种标准进行评判,最终返回boolean值
Predicate的test 接收T类型的数据,返回 boolean 类型,即对数据进行某种规则的评判,如果符合则返回true,否则返回false;Predicate接口还提供了 and、negate、or,与 取反 或等,isEqual 判断两个参数是否相等等默认函数。
Supplier
生产、提供数据:get方法返回一个T类数据,可以提供重复的数据,或者随机种子都可以,就这么简单。
Collectors收集器
- 数据收集:set、map、list
- 聚合归约:统计、求和、最值、平均、字符串拼接、规约
- 前后处理:分区、分组、自定义操作
数据收集
- Collectors.toCollection() 将数据转成Collection,只要是Collection 的实现都可以,例如ArrayList、HashSet ,该方法接受一个Collection 的实现对象或者说Collection 工厂的入参。
- Collectors.toList()和Collectors.toSet() 其实和Collectors.toCollection() 差不多,只是指定了容器的类型,默认使用ArrayList 和 HashSet。本来我以为这两个方法的内部会使用到Collectors.toCollection(),结果并不是,而是在内部new了一个CollectorImpl。
- Collectors.toMap() 和Collectors.toConcurrentMap(),见名知义,收集成Map和ConcurrentMap,默认使用HashMap和ConcurrentHashMap。这里toConcurrentMap()是可以支持并行收集的,这两种类型都有三个重载方法,不管是Map 还是ConcurrentMap,他们和Collection的区别是Map 是K-V 形式的,所以在收集成Map的时候必须指定收集的K(依据)。这里toMap()和toConcurrentMap() 最少参数是,key的获取,要存的value。
聚合归约
-
Collectors.joining(),拼接,有三个重载方法,底层实现是StringBuilder,通过append方法拼接到一起,并且可以自定义分隔符(这个感觉还是很有用的,很多时候需要把一个list转成一个String,指定分隔符就可以实现了,非常方便)、前缀、后缀。
-
Collectors.counting() 统计元素个数,这个和Stream.count() 作用都是一样的,返回的类型一个是包装Long,另一个是基本long,但是他们的使用场景还是有区别的
-
Collectors.minBy()、Collectors.maxBy() 和Stream.min()、Stream.max() 作用也是一样的,只不过Collectors.minBy()、Collectors.maxBy()适用于高级场景。
-
Collectors.summingInt()、Collectors.summarizingLong()、Collectors.summarizingDouble() 这三个分别用于int、long、double类型数据一个求总操作,返回的是一个SummaryStatistics(求总),包含了数量统计count、求和sum、最小值min、平均值average、最大值max。虽然IntStream、DoubleStream、LongStream 都可以是求和sum 但是也仅仅只是求和,没有summing结果丰富。如果要一次性统计、求平均值什么的,summing还是非常方便的。
-
Collectors.averagingInt()、Collectors.averagingDouble()、Collectors.averagingLong() 求平均值,适用于高级场景
-
Collectors.reducing() 好像也和Stream.reduce()差不多,也都是规约操作。其实Collectors.counting() 就是用reducing()实现的
前后处理
-
Collectors.groupingBy()和Collectors.groupingByConcurrent(),这两者区别也仅是单线程和多线程的使用场景。为什么要groupingBy归类为前后处理呢?groupingBy 是在数据收集前分组的,再将分好组的数据传递给下游的收集器。这是 groupingBy最长的参数的函数classifier 是分类器,mapFactory map的工厂,downstream下游的收集器,正是downstream 的存在,可以在数据传递给下游之前做很多的骚操作。
-
Collectors.partitioningBy() 字面意思话就叫分区好了,但是partitioningBy最多只能将数据分为两部分,因为partitioningBy分区的依据Predicate,而Predicate只会有true 和false 两种结果,所有partitioningBy最多只能将数据分为两组。partitioningBy除了分类器与groupingBy 不一样外,其他的参数都相同。
-
Collectors.mapping() 可以自定义要收集的字段
-
Collectors.collectingAndThen()收集后操作,如果你要在收集数据后再做一些操作,那么这个就非常有用了。
IntStream
https://www.jianshu.com/p/461429a5edc9
使用StreamTrace
StreamTrace只有在debug模式下才能使用,当在Stream代码上设置断点后,启动debug,点击流按钮,如图所示。
点击后,默认Split 模式显示。
可以点击左下方按钮切换到FlatMode模式,当然也可以再切换回去。