Java 8 - 流意识形态

2022-09-04 01:09:16

我最近开始玩Java 8,之前在Haskell/Scala中做过一些零碎的事情。我试图在Java中使用高阶函数,例如或,我正在努力理解推动一切走向意识形态的动机是什么。我知道它给出了很好的,一般的抽象,它应该是懒惰的,但让我们考虑一个非常简单,常见的例子:mapforEachStream

list.map(x -> do_sth(x));

非常常见的成语,期望这返回一个.现在,在Java 8中,我需要做这样的sth:List<T>

list.stream().map(x -> doSth(x)).collect(Collectors.toList())

现在,据我所知,在调用 collect 之前,流不会应用地图,因此将有一个通过引擎盖下的集合。我不明白的是,为什么那些常见的地图用例,例如,列表不会添加到相应的接口中?我在这里是否缺少一个基础设计决策?map.toList()list.groupBy()


答案 1

一些新方法已直接添加到各种集合中,这些集合急切地对这些集合执行突变操作。例如,要对列表的每个元素运行函数,将原始元素替换为返回值,请使用 。其他示例包括 、 和 。List.replaceAll(UnaryOperator)Collection.removeIf(Predicate)List.sort()Map.replaceAll(BiFunction)

相比之下,有一堆新方法添加到Stream中,例如过滤器,地图,跳过,限制,排序,独特等。其中大多数是懒惰的,它们不会改变源,而是将元素传递到下游。我们确实考虑过将这些直接添加到集合类中。在这一点上,出现了几个问题。我们如何区分急切的变异操作和懒惰的流生产操作?重载是困难的,因为它们具有不同的返回类型,因此它们必须具有不同的名称。如何将此类操作链接起来?急切的操作必须生成集合来存储中间结果,这可能非常昂贵。生成的集合 API 将具有令人困惑的混合,包括急切、变异和懒惰、非变异的方法。

第二个考虑因素是添加默认方法的潜在不兼容。向接口添加默认方法的最大风险是与该接口实现上的现有方法发生名称冲突。如果具有相同名称和参数(通常没有参数)的方法具有不同的返回类型,则这是不可避免的不兼容性。出于这个原因,我们一直不愿意添加大量默认方法。

出于这些原因,我们决定将懒惰、非变异的方法全部保留在流 API 中,代价是需要额外的方法调用 stream() 和 collect() 来桥接集合和流。对于一些常见情况,我们直接向集合接口添加了急切的变异调用,例如我上面列出的那些。

有关进一步讨论,请参阅 lambdafaq.org


答案 2