为什么Stream操作与收集器重复?

hol*_*ava 3 java java-8 java-stream

请允许我提出一些投诉,也许这很有意思,但我想描述:" 为什么会提出这个问题? ".我已经回答问题,不同于其他在这里,这里这里昨晚.

在深入研究之后,我发现StreamCollector之间有许多重复的逻辑违反了不重复自己的原则,例如:Stream#map&Collectors#mapping,Stream#filter&Collectors#filtering in jdk-9 and.等等

但似乎合理,因为Stream遵守Tell,不要问原则/ Demeter法收藏家遵守继承原则.

我只能想到为什么Stream操作与Collector s 重复的几个原因如下:

  1. 我们不关心如何在大环境中创建Stream.在这种情况下,Stream操作比Collector更有效和更快,因为它可以简单地将Stream映射到另一个Stream,例如:

    consuming(stream.map(...));
    consuming(stream.collect(mapping(...,toList())).stream());
    
    void consuming(Stream<?> stream){...}
    
    Run Code Online (Sandbox Code Playgroud)
  2. 收集器功能更强大,可以将收集器组合在一起收集流中的元素,但Stream只提供一些有用/高度使用的操作.例如:

    stream.collect(groupingBy(
      ..., mapping(
            ..., collectingAndThen(reducing(...), ...)
           )
    ));
    
    Run Code Online (Sandbox Code Playgroud)
  3. 在执行一些更简单的工作时,操作比收集器更具表现力,但它们比收集器更慢,因为它将为每个操作创建一个新流,而Stream收集器更重,更抽象.例如:

    stream.map(...).collect(collector);
    stream.collect(mapping(..., collector));
    
    Run Code Online (Sandbox Code Playgroud)
  4. 收集器不能将短路终端操作应用为Stream.例如:

    stream.filter(...).findFirst();
    
    Run Code Online (Sandbox Code Playgroud)

是否有人能够提出其他不利/优势,为什么Stream操作与收集器重复?我想重新理解它们.提前致谢.

Hol*_*ger 7

链接专用终端流操作可能被认为更具表现力的是那些用于链接方法调用而不是组合收集器工厂调用的"LISP样式".但它也允许流实现的优化执行策略,因为它知道实际操作而不仅仅是看到Collector抽象.

另一方面,当您自己命名时,Collector可以组合s,允许在不再可能进行流操作的地方执行嵌入在另一个收集器中的这些操作.我想,这种镜像只有在Java 8开发的后期才会变得明显,这就是为什么某些操作缺少对应的原因,比如filtering或者flatMapping只有Java 9才会出现这种情况.所以,让两个不同的API做类似的事情. ,不是在开发开始时做出的设计决定.

  • 收集器并不意味着取代流操作.但是使用Java 9,所有无状态中间操作都可用作收集器,`mapping`,`flatMapping`,`filtering`,(和gatherAndThen`,如果你想计算它).同样,几乎所有的非短路操作都有一个对应的,`maxBy`,`minBy`,`reducing`,`counting`.剩下的就是短路操作和`forEach`和`toArray`,它们与收藏家的工作根本不同. (3认同)
  • 我从未说过"LISP风格"是不合理的.我所说的是,Java开发人员更习惯于链式调用风格. (2认同)

Era*_*ran 6

Collectors看似重复的方法Stream方法是提供额外的功能.当与其他Collectors 组合使用时,它们是有意义的.

例如,如果我们考虑Collectors.mapping(),最常见的用途是将其传递给Collectors.groupingBy Collector.

考虑这个例子(取自Javadoc):

List<Person> people = ...
Map<City, Set<String>> namesByCity
         = people.stream().collect(groupingBy(Person::getCity, TreeMap::new,
                                              mapping(Person::getLastName, toSet())));
Run Code Online (Sandbox Code Playgroud)

mapping这里用于将Collection每个组的值的元素类型转换PersonString.

没有它(和toSet() Collector),输出就是Map<City, List<Person>>.

现在,你当然可以map一个Stream<Person>一个Stream<String>使用people.stream().map(Person::getLastName),但随后你会通过一些其他财产损失的能力,这些小组最后一个名称Person(Person::getCity在这个例子中).