是否允许/build议重用收集器?

我在代码中有很多地方可以做:

someStream.collect(Collectors.toList()) 

Collectors.toList()在每个用途上创build一个新的收集器。

这就引出了一个问题,如果允许和可取的做一些事情,比如:

 private final static Collector<…> TO_LIST = Collectors.toList() 

对于我使用的每种types,然后使用单个收集器:

 someStream.collect(TO_LIST) 

当需要收集器时。

由于收集器是无状态的,只是function和特性的集合,所以我认为它应该起作用,但是OTOH, Collectors.toList()会在每次调用时创build一个新的CollectorImpl<>

重复使用收集器有什么缺点?

我认为这更像是一个风格问题,但是让我们来思考一下:

  • 使用这样的CONST收集器对象似乎是常见的做法。 从这个意义上说:这样做可能会让一些读者感到惊讶,令人惊讶的读者很less是一件好事。
  • 然后:几个代码可以被“复制”(可能不应该避免代码重复); 但仍然是:指向一个独特的收集器对象可能会使您更难以重新分解或重用您的stream构造。
  • 除此之外:你自己说出来了; 收集器的重用依赖无状态的实现。 所以你让自己依赖任何无状态的实现。 可能不是问题; 但也许有一个风险要记住!
  • 可能更重要的是:从表面上看,你的想法看起来是优化的一个很好的手段。 但是好的; 当你担心使用stream的“性能效应”时,那么最终收集器的单个对象创build将“不能削减”!

我的意思是:如果你担心“浪费”的performance, 您宁愿查看使用stream的每一行代码,以确定该stream是否使用“足够”的对象来certificate首先使用stream。 这些溪stream有相当的开销!

长话短说:java社区还没有find“标准最佳实践”的stream; 因此我的(个人)这两个分:更喜欢那些“大家”使用的模式 – 避免做你自己的事情。 特别是在“性能相关”的时候。

由于Collector基本上是四个函数和特征标记的容器,所以重用它没有问题,但也很less有任何优势,因为如果不是优化器完全移除这种轻量级对象对内存pipe理的影响可以忽略不计无论如何。

不像Collector那样重复使用Collector的主要原因是,你不能以一种安全的方式去做。 当为任意types的List提供一个收集器时,您需要不经过检查的操作来总是交出相同的Collector实例。 如果您将Collector存储在正确types的variables中,而不使用未经检查的操作,则只能将其用于一种types的List ,以保留该示例。

Collections.emptyList()等的情况下,JRE开发者采取了不同的方式,但是在引入generics之前,常量EMPTY_LISTEMPTY_MAPEMPTY_SET已经存在了,我认为它们比less数可caching的Collectors ,这是超过三十内置collections家,只有四个特殊情况下,由于其function参数不能被caching。 由于函数参数通常通过lambdaexpression式实现,lambdaexpression式产生未指定的身份/相等的对象,所以将它们映射到收集器实例的caching将具有不可预知的效率,但很可能远不如内存pipe理器处理临时实例那么有效。

图书馆提供获取有用对象的工厂方法是一个很好的做法。 由于库提供了这样一个方法: Collectors.toList()让库在每次请求对象时决定是否创build一个新实例,而不是篡改库,这样做也是一个很好的做法可读性和在实施变化时冒未来的风险。

这个被添加到GhostCat和Holger的答案中作为一个支持性的论点:)

只是一个小小的附注,@Holger在回答关于优化器是聪明的并且完全replace这个构造的时候说的是完全可行的,这就是所谓的scalar replacement 。 当一个方法内使用的对象被去构造,并且它的字段stack allocated like normal local variablesstack allocated like normal local variables 。 因此,最终的Collector可能不会在JVM级别作为对象进行处理。 这将发生在JIT time

使用单个静态对象代替正在运行的一个经典问题是可变性。 对Java 8源代码的快速扫描突出显示了Set<Characteristics>字段是一个可能的问题。

很显然,某些代码可能会在某处做类似的事情:

 private final static Collector<Object, ?, List<Object>> TO_LIST = Collectors.toList(); public void test() { // Any method could do this (no idea why but it should be possible). TO_LIST.characteristics().add(Collector.Characteristics.IDENTITY_FINISH); } 

可能会在全球范围内改变每个使用TO_LIST的function,这可能会产生非常模糊的错误。

所以恕我直言 – 不要!

这将是一个过早优化的情况。 对象创build相当便宜。 在普通的笔记本电脑上,我希望能够创build每秒10M-50M的对象。 考虑到这些数字,整个练习变得毫无意义。