《Java 8 函数式编程》笔记2
流
从外部迭代到内部迭代
使用 for
循环统计来自美国的艺术家:
1 | int count = 0; |
for
循环本质是封装了迭代的语法糖,外部迭代:
1 | int count = 0; |
外部迭代本质上是一种串行化操作。
使用内部迭代改写:
1 | // stream() 方法和上面的 iterator() 作用一样 |
注:Stream
是用函数式编程方式在集合类上进行复杂操作的工具
内部迭代的实现机制
只过滤,不计数:
1 | allArtists.stream() |
filter
只是刻画出了 Stream
,并没有产生新的集合。
- 这些不产生新集合方法叫:惰性求值方法;
- 像
count
这样最终会从Stream
产生值的方法叫:及早求值方法。
即使在 filter
过滤器中加上 println
,也不会输出任何信息:
1 | allArtists.stream() |
但只要加入一个拥有终止操作的流,艺术家的名字就会被输出:
1 | allArtists.stream() |
如何判断一个操作是惰性求值还是及早求值?
看它的返回值:
- 返回值是
Stream
:惰性求值; - 返回值是另一个值或
null
:及早求值。
常用的流操作
collect()
由 Stream
里的值生成一个 List
、Set
、Map
或其他。
比如,生成 List
:
1 | // 使用 Stream 的 of 方法: |
map
将一个流中的值转换为一个新的流。
该 lambda
表达式的函数接口是 Function
。
比如,将一组字符串都转为大小形式:
1 | List<String> collected = Stream.of("a", "b", "abc") |
filter
保留 Stream
中符合条件的元素,而过滤掉其他的。
该 lambda
表达式的函数接口是 Predicate
。
比如,找出一组字符串中以数字开头的字符串:
1 | List<String> startWithDigits = Stream.of("1a", "b", "abc") |
flatMap
将多个 Stream
连接成一个 Stream。
该 lambda
表达式的函数接口是 Function
,返回值是 Stream
。
比如,一个包含多个列表的 Stream
连接成只有一个列表的 Stream
:
1 | List<Integer> together = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4)) |
max 和 min
找出 Stream
中的最大最小值。
比如,找出播放长度最短的曲目:
1 | List<Track> tracks = Arrays.asList( |
reduce
从一组值中生成一个值,上面用到的 count
、min
和 max
方法都属于 reduce
操作。因为常用而被纳入标准库。
比如,求和:
1 | // 0 是初始值,acc 是累加器 |
阶乘:
1 | BigInteger k = Stream.iterate(BigInteger.ONE, x -> x.add(BigInteger.ONE)) |
整合操作
举例说明如何把问题分解成简单的 Stream
操作:
如何找出某张专辑上乐队所有成员的国籍?
将问题分解:
- 找出专辑上的所有表演者
- 分辨出哪些表演者是乐队
- 找出乐队每个中每个成员的国籍
- 将找出的国籍放在一个集合里
找出对应的 Stream API
:
- 专辑
Album
类有getMusicians
方法,该方法返回一个Stream
对象,包含整张专辑中所有的表演者 - 使用
filter
方法对表演者进行过滤,只保留乐队 - 使用
flatMap
方法将乐队成员加入流中 - 使用
map
方法将成员映射为其所属国家 - 使用
collect
方法将找出的国籍放到集合里
1 | Set<String> origins = album.getMusicians() |