为什么Stream操作与收集器重复?
请允许我提出一些投诉,也许这很有意思,但我想描述一下:“ 为什么要提出这个问题? ”。 我在这里回答的问题与其他问题不同,昨晚在这里和这里 。
在我深入研究之后,我发现Stream和Collector之间有许多重复的逻辑违反了不重复自己的原则,例如: Stream#map & Collectors#mapping , Stream#filter & Collectors#filtering in jdk-9 and。等等
但似乎合理,因为Stream遵守Tell,不要问 Demeter的原则/ 法则和collections家遵守inheritance原则的构成。
我只能想到为什么Stream操作与Collector s重复的几个原因如下:
-
我们不关心如何在大环境中创建Stream 。 在这种情况下, Stream操作比Collector更有效和更快,因为它可以简单地将Stream映射到另一个Stream ,例如:
consuming(stream.map(...)); consuming(stream.collect(mapping(...,toList())).stream()); void consuming(Stream stream){...}
-
收集器function更强大,可以将收集器组合在一起收集流中的元素,但Stream只提供一些有用/高度使用的操作。 例如:
stream.collect(groupingBy( ..., mapping( ..., collectingAndThen(reducing(...), ...) ) ));
-
在执行一些更简单的工作时, 流操作比收集器更具表现力,但它们比收集器更慢,因为它将为每个操作创建新流,并且Stream比收集器更重和更抽象。 例如:
stream.map(...).collect(collector); stream.collect(mapping(..., collector));
-
收集器不能将短路终端操作应用为Stream 。 例如:
stream.filter(...).findFirst();
是否有人可以提出其他不利/优势,为什么Stream操作与收集器重复? 我想重新理解它们。 提前致谢。
链接专用终端流操作的链接可能被用于链接方法调用的那些更具表现力,而不是组合的收集器工厂调用的“LISP样式”。 但它也允许流实现的优化执行策略,因为它知道实际操作而不仅仅是看到Collector
抽象。
另一方面,正如您自己命名的那样,可以组合Collector
,允许在不再可能进行流操作的地方执行嵌入另一个收集器的这些操作。 我想,这种镜像只在Java 8开发的后期才变得明显,这就是为什么有些操作缺少对应的原因,比如filtering
或flatMapping
,它们只存在于Java 9中。所以,有两个不同的API在做类似的事情,不是在开发开始时做出的设计决定。
似乎与Stream
方法重复的Collectors
方法提供了额外的function。 当与其他Collector
结合使用时,它们是有意义的。
例如,如果我们考虑Collectors.mapping()
,最常见的用途是将它传递给Collectors.groupingBy
Collector
。
考虑这个例子(取自Javadoc):
List people = ... Map> namesByCity = people.stream().collect(groupingBy(Person::getCity, TreeMap::new, mapping(Person::getLastName, toSet())));
这里使用mapping
将每个组的Collection
的元素类型从Person
为String
。
如果没有它(和toSet()
Collector
),输出将是Map
。
现在,您当然可以使用people.stream().map(Person::getLastName)
将Stream
map
到Stream
,但是您将失去通过Person
其他属性对这些姓氏进行分组的function(本例中为Person::getCity
)。