资讯专栏INFORMATION COLUMN

「Java 8 函数式编程」读书笔记——高级集合类和收集器

imingyu / 691人阅读

摘要:本章是该书的第五章主要讲了方法引用和收集器方法引用形如这样的表达式可以简写为这种简写的语法被称为方法引用方法引用无需考虑参数因为一个方法引用可以在不同的情况下解析为不同的表达式这依赖于的推断方法引用的类型方法引用可以分为四类引用静态方法

本章是该书的第五章, 主要讲了方法引用和收集器

方法引用

形如:

artist -> artist.getName()
(String arg) -> arg.length()

这样的表达式, 可以简写为:

Artist::getName
String::length

这种简写的语法被称为方法引用. 方法引用无需考虑参数, 因为一个方法引用可以在不同的情况下解析为不同的Lambda表达式, 这依赖于JVM的推断.

方法引用的类型

方法引用可以分为四类:

引用静态方法: ClassName::staticMethodName, 比如: String.valueOf

引用特定实例方法: object::instanceMethodName, 比如: str::toString

引用特定类型的任意对象的实例方法: ClassName::instanceMethodName, 比如: String::length

引用构造方法: ClassName::new, 比如: String::new

元素顺序

当我们对集合进行操作时, 有时希望是按照一定的顺序来操作, 而有时又希望是乱序的操作. 有两个方法可以帮助我们进行顺序的操作.

乱序

BaseStream.unordered()方法可以打乱顺序, 科技将本来有序的集合变成无序的集合

排序

Stream.sorted方法有两个签名, 一个无参, 一个有参数Comparator comparator

无参的方法要求T实现了Comparable接口

有参方法需要提供一个比较器

收集器

收集器是一种通用的, 从流中生成复杂值的结构. 将其传给collect方法, 所有的流就都可以使用它. 而下面提到的单个收集器, 都可以使用reduce方法模拟.

转换成集合

我们可以使用Collectors中的静态方法toList() toSet()等, 将流收集为ListSet

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, 然后就可以对相应的数据进行处理了.

数据分组

groupingBy方法可以将流分成多个List, 而不仅仅是两个, 接收一个Lambda表达式作为参数, 其返回值作为key, 最后的结果也是一个Map, 形如Map. 这一方法类似于SQL中的group by

生成字符串

如果要从流中得到字符串, 可以在得到Stream之后使用Collectors.joining方法收集. 该方法接收3个String参数, 分别是分隔符 前缀 后缀

artists.stream()
  .map(Artist::getName)
  .collect(Collectors.joining(",", "[", "]"));
组合收集器

我们可以将收集器组合起来, 达到更强的功能. 书上举了两个栗子

例一

public Map numberOfAlbums(Stream albums) {
  return albums
    .collect(
    groupingBy(Album::getMainMusicina, counting()));
}

这个方法的目的是统计每个歌手的作品数目. 如果不组合收集器, 我们先用groupingBy得到一个Map>之后, 还要去遍历Map得到统计数目, 增加了代码量和性能开销.

上面的counting方法类似于count方法, 作用于List的流上.

例二

public Map> nameOfAlbums(Stream albums) {
  return albums
    .collect(
    groupingBy(Album::getMainMusician,
              mapping(Album::getName, toList())));
}

这个方法的目的是得到每个歌手的作品名称列表. 如果不组合收集器, 我们将会先得到一个Map>. 然而, 我们只想得到作品名称, 也就是一个List, 组合mapping收集器可以帮助我们实现效果.

mapping收集器的功能类似于map, 将一种类型的流转换成另一种类型. 所以类似的, mapping并不知道要把结果收集成什么数据结构, 它的第二个参数就会接收一个普通的收集器, 比如这里的toList, 来完成收集.

这里的countingmapping是我们用到的第二个收集器, 用于收集最终结果的一个子集, 这些收集器叫做下游收集器.

定制收集器

定制收集器看起来麻烦, 其实抓住要点就行了.

使用reduce方法

前面说过, 这些收集器都可以使用reduce方法实现, 我们定制收集器, 实际上就是为reduce方法编写三个参数, 分别是:

identity

accumulator

combiner

关于这三个参数的意义, 如果不太理解, 可以看看这个答案: https://segmentfault.com/q/1010000004944450

我们可以设计一个类, 为这三个参数设计三个方法, 再提供一个方法用于获取目标类型(如果这个类就是目标类型的话, 可以不提供这个方法)

实现Collector接口

如果不想显式的使用reduce方法, 我们只需要提供一个类, 实现Collector接口.

该接口需要三个泛型参数, 依次是:

待收集元素的类型

累加器的类型

最终结果的类型

需要实现的方法有:

supplier: 生成初始容器

accumulator: 累加计算方法

combiner: 在并发流中合并容器

finisher: 将容器转换成最终值

characteristics: 获取特征集合

多数情况下, 我们的容器器和我们的目标类型并不一致, 这时, 需要实现finisher方法将容器转化为目标类型, 比如调用容器的toString方法.

有时我们的目标类型就是我们的容器, finisher方法就不需要对容器做任何操作, 而是通过设置characteristicsIDENTITY_FINISH, 使用框架提供的优化得到结果.

详细讲解可以参见http://irusist.github.io/2016/01/04/Java-8%E4%B9%8BCollector/

Map新增方法

Java 8Map新增了很多方法, 可以通过搜索引擎轻松找到相关文章. 这里举几个书中提到的相关方法.

V computeIfAbsent(K key, Function mappingFunction)

V computeIfPresent(K key, BiFunction remappingFunction)

V compute(K key, BiFunction remappingFunction)

这三个方法类似, 都是根据key来处理, 只是Lambda表达式的执行条件不同, 从函数名就可以看出来. 不过要注意Lambda表达式的参数, 第一个方法的Lambda只需要一个参数key, 后面两个方法的Lambda需要两个参数keyvalue, 而compute方法的Lambda中的value参数可能为null.

V merge(K key, V value, BiFunction remappingFunction)

此方法用于合并value, 新value在第二个参数给出. Lambda表达式规定合并方法, 其两个参数依次是oldValuenewValue, oldValue是原Mapvalue, 可能为空; newValuemerge方法的第二个参数.

void forEach(BiConsumer action)

通过forEach方法, 不再需要使用外部迭代来遍历Map.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66626.html

相关文章

  • Java8实战》-第六章读书笔记(用流收集数据-01)

    摘要:收集器用作高级归约刚刚的结论又引出了优秀的函数式设计的另一个好处更易复合和重用。更具体地说,对流调用方法将对流中的元素触发一个归约操作由来参数化。另一个常见的返回单个值的归约操作是对流中对象的一个数值字段求和。 用流收集数据 我们在前一章中学到,流可以用类似于数据库的操作帮助你处理集合。你可以把Java 8的流看作花哨又懒惰的数据集迭代器。它们支持两种类型的操作:中间操作(如 filt...

    EscapedDog 评论0 收藏0
  • java 8 实战》读书笔记 -第四章 引入流

    摘要:第四章引入流一什么是流流是的新成员,它允许你以声明性方式处理数据集合通过查询语句来表达,而不是临时编写一个实现。 第四章 引入流 一、什么是流 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码。 下面两段代码都是用来返回低...

    jeyhan 评论0 收藏0
  • Java 8 函数编程读书笔记——流

    摘要:本文是函数式编程第三章的读书笔记,章名为流。正确使用表达式明确要达成什么转化,而不是说明如何转化没有副作用只通过函数的返回值就能充分理解函数的全部作用函数不会修改程序或外界的状态获取值而不是变量避免使用数组逃过的追杀,应该考虑优化逻辑 本文是「Java 8 函数式编程」第三章的读书笔记,章名为流。本章主要介绍了外部迭代与内部迭代以及常用的高阶函数。 外部迭代与内部迭代 外部迭代 过去我...

    qpwoeiru96 评论0 收藏0
  • Java8新特性总览

    摘要:新特性总览标签本文主要介绍的新特性,包括表达式方法引用流默认方法组合式异步编程新的时间,等等各个方面。还有对应的和类型的函数连接字符串广义的归约汇总起始值,映射方法,二元结合二元结合。使用并行流时要注意避免共享可变状态。 Java8新特性总览 标签: java [TOC] 本文主要介绍 Java 8 的新特性,包括 Lambda 表达式、方法引用、流(Stream API)、默认方...

    mayaohua 评论0 收藏0
  • Java编程思想》读书笔记-对象导论

    摘要:而面向对象则是向程序员提供表示问题空间中元素的工具,我们将问题空间中的元素及其在解空间中的表示称为对象。为什么要把对象看作是服务提供者呢这是将问题分解为对象集合的一种合理方式。职能太多,可能会导致对象的内聚性降低。在试图将子类对象当作其基类 计算机是头脑延伸的工具,是一种不同类型的表达媒体。本文以背景性的和补充性的材料,介绍包括开发方法概述在内的面向对象程序设计(Object-orie...

    NickZhou 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<