本章是该书的第五章, 主要讲了方法引用和收集器java
形如:git
artist -> artist.getName() (String arg) -> arg.length()
这样的表达式, 能够简写为:github
Artist::getName String::length
这种简写的语法被称为方法引用
. 方法引用无需考虑参数, 由于一个方法引用能够在不一样的状况下解析为不一样的Lambda
表达式, 这依赖于JVM
的推断.segmentfault
方法引用能够分为四类:安全
引用静态方法: ClassName::staticMethodName
, 好比: String.valueOf
数据结构
引用特定实例方法: object::instanceMethodName
, 好比: str::toString并发
引用特定类型的任意对象的实例方法: ClassName::instanceMethodName
, 好比: String::length
app
引用构造方法: ClassName::new
, 好比: String::new
框架
当咱们对集合进行操做时, 有时但愿是按照必定的顺序来操做, 而有时又但愿是乱序的操做. 有两个方法能够帮助咱们进行顺序的操做.ide
BaseStream.unordered()
方法能够打乱顺序, 科技将原本有序的集合变成无序的集合
Stream.sorted
方法有两个签名, 一个无参, 一个有参数Comparator<? super T> comparator
无参的方法要求T
实现了Comparable
接口
有参方法须要提供一个比较器
收集器是一种通用的, 从流中生成复杂值的结构. 将其传给collect
方法, 全部的流就均可以使用它. 而下面提到的单个收集器, 均可以使用reduce
方法模拟.
咱们可使用Collectors
中的静态方法toList()
toSet()
等, 将流收集为List
或Set
stream.collect(toList()) stream.collect(toSet())
咱们不须要关心具体使用的是哪种具体的实现, Stream
类库会为咱们选择. 由于咱们能够利用Stream
进行并行数据处理, 因此选择是否线程安全的集合十分重要.
固然咱们也能够指定使用哪种实现来进行收集:
stream.collect(toCollection(ArrayList::new))
Collectors
类提供了不少的方法用于转化值, 好比counting
maxBy
minBy
等等, 能够查看javadoc
了解.
目前了解到的是, 这三个方法均可以使用Stream
中的count
max
min
方法代替, 而不须要做为collect
方法的参数
有时咱们想按照一个条件把数据分红两个部分, 而不是只获取符合条件的部分, 这时可使用partitioningBy
方法收集. 将它传入collect
方法, 能够获得一个Map<Boolean, List>
, 而后就能够对相应的数据进行处理了.
groupingBy
方法能够将流分红多个List
, 而不单单是两个, 接收一个Lambda
表达式做为参数, 其返回值做为key
, 最后的结果也是一个Map
, 形如Map<String, List>
. 这一方法相似于SQL
中的group by
若是要从流中获得字符串, 能够在获得Stream<String>
以后使用Collectors.joining
方法收集. 该方法接收3个String
参数, 分别是分隔符
前缀
后缀
artists.stream() .map(Artist::getName) .collect(Collectors.joining(",", "[", "]"));
咱们能够将收集器组合起来, 达到更强的功能. 书上举了两个栗子
例一
public Map<Artist, Long> numberOfAlbums(Stream<Album> albums) { return albums .collect( groupingBy(Album::getMainMusicina, counting())); }
这个方法的目的是统计每一个歌手的做品数目. 若是不组合收集器, 咱们先用groupingBy
获得一个Map<Artist, List<Album>>
以后, 还要去遍历Map
获得统计数目, 增长了代码量和性能开销.
上面的counting
方法相似于count
方法, 做用于List<Album>
的流上.
例二
public Map<Artist, List<String>> nameOfAlbums(Stream<Album> albums) { return albums .collect( groupingBy(Album::getMainMusician, mapping(Album::getName, toList()))); }
这个方法的目的是获得每一个歌手的做品名称列表. 若是不组合收集器, 咱们将会先获得一个Map<Artist, List<Album>>
. 然而, 咱们只想获得做品名称, 也就是一个List<String>
, 组合mapping
收集器能够帮助咱们实现效果.
mapping
收集器的功能相似于map
, 将一种类型的流转换成另外一种类型. 因此相似的, mapping
并不知道要把结果收集成什么数据结构, 它的第二个参数就会接收一个普通的收集器, 好比这里的toList
, 来完成收集.
这里的counting
和mapping
是咱们用到的第二个收集器, 用于收集最终结果的一个子集, 这些收集器叫作下游收集器
.
定制收集器看起来麻烦, 其实抓住要点就好了.
reduce
方法前面说过, 这些收集器均可以使用reduce
方法实现, 咱们定制收集器, 实际上就是为reduce
方法编写三个参数, 分别是:
identity
accumulator
combiner
关于这三个参数的意义, 若是不太理解, 能够看看这个答案: https://segmentfault.com/q/1010000004944450
咱们能够设计一个类, 为这三个参数设计三个方法, 再提供一个方法用于获取目标类型(若是这个类就是目标类型的话, 能够不提供这个方法)
Collector
接口若是不想显式的使用reduce
方法, 咱们只须要提供一个类, 实现Collector
接口.
该接口须要三个泛型参数, 依次是:
待收集元素的类型
累加器的类型
最终结果的类型
须要实现的方法有:
supplier
: 生成初始容器
accumulator
: 累加计算方法
combiner
: 在并发流中合并容器
finisher
: 将容器转换成最终值
characteristics
: 获取特征集合
多数状况下, 咱们的容器器和咱们的目标类型并不一致, 这时, 须要实现finisher
方法将容器转化为目标类型, 好比调用容器的toString
方法.
有时咱们的目标类型就是咱们的容器, finisher
方法就不须要对容器作任何操做, 而是经过设置characteristics
为IDENTITY_FINISH
, 使用框架提供的优化获得结果.
详细讲解能够参见http://irusist.github.io/2016/01/04/Java-8%E4%B9%8BCollector/
Map
新增方法Java 8
为Map
新增了不少方法, 能够经过搜索引擎轻松找到相关文章. 这里举几个书中提到的相关方法.
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
V computeIfPresent(K key, BiFunction<? super K, ? super V, extends V> remappingFunction)
V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
这三个方法相似, 都是根据key
来处理, 只是Lambda
表达式的执行条件不一样, 从函数名就能够看出来. 不过要注意Lambda
表达式的参数, 第一个方法的Lambda
只须要一个参数key
, 后面两个方法的Lambda
须要两个参数key
和value
, 而compute
方法的Lambda
中的value
参数可能为null
.
V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
此方法用于合并value
, 新value
在第二个参数给出. Lambda
表达式规定合并方法, 其两个参数依次是oldValue
和newValue
, oldValue
是原Map
的value
, 可能为空; newValue
为merge
方法的第二个参数.
void forEach(BiConsumer<? super K, ? super V> action)
经过forEach
方法, 再也不须要使用外部迭代来遍历Map
.