为什么flatMap操作需要一个返回Stream的函数而不是一个返回Collection的函数?有什么特别的原因迫使用户手动进行流转换吗?
阅读源代码示例,我可以看到这种方式的兼容性可以扩展到数组,但不会超载flatMap实现相同的结果?
// Java 8 source code example:
Stream<String> words = lines.flatMap(line -> Stream.of(line.split(" +")));
在哪些用例中更好地显式显示流处理?
示例:为什么我必须这样做
Map<String, List<String>> map = new HashMap<String, List<String>>();
List<String> flatList = map.entrySet().stream().flatMap(e -> e.getValue().stream()).collect(Collectors.toList());
而不是这个?
Map<String, List<String>> map = new HashMap<String, List<String>>();
List<String> flatList = map.entrySet().stream().flatMap(Map.Entry::getValue).collect(Collectors.toList());
为什么
flatMap()
操作需要一个返回Stream
的函数而不是一个返回Collection
的函数?
有很多原因:
-
流是迭代的方式,即我们不将数据存储在流中,其目的是在数据源上惰性地迭代,可以是
String
, Array, IO-Stream等。 -
其次,流操作分为两组:终端,用于产生结果并终止流管道的执行(即不可能在终端操作之后应用任何操作),而中间操作,用于转换流。中间操作总是惰性。流一个接一个地从源获取元素,并惰性地处理它们,也就是说,操作只在需要时发生。不要用嵌套的
for
循环链创建新的流,它们的行为不同。每个中间操作产生一个新的流。
这是来自API文档的引用:
流与集合在几个方面不同:
没有存储。流是而不是数据结构存储元素;相反,它从数据结构等源传递元素,一个数组,一个生成器函数,或一个I/O通道,通过管道
Laziness-seeking。许多流操作,如过滤、映射或重复删除,都可以惰性地实现,从而暴露优化的机会。例如,"查找第一个字符串"有三个连续的元音"不需要检查所有输入字符串。流操作分为中间(流生成)操作和终端(产生价值或副作用)操作。中间操作总是惰性的.
- 由于流是数据源的内部迭代器,可以具有不同的性质(不一定是
Collectoin
),因此flatMap()
期望数据具有可预测的统一形状是合理的,不是Array, Collection, Iterable等,而是另一个内部迭代器,即另一个流,所以如何处理它是显而易见的。
你能想到的任何选项都不那么直观。如果flatMap()
以这样一种方式实现,那么它将期望一个函数产生Collection
,您将如何处理字符串,数组,IO-Streams,Iterable
的各种实现?通过将数据转储到Collection中—这不是一个选项。同样的问题也会出现,如果我们想象flatMap()
需要Iterable
,我们如何从String
产生Iterable
?流的设计是通用的。
我怀疑你对flatMap()
的判断有偏见,因为你不习惯它。当你接受流是一个内部迭代器的想法时,将数据期望函数扁平化从而产生另一个迭代器的操作会被认为更直观。