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

时间:2017-05-09 07:42:24

标签: java collections java-8 java-stream

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

someStream.collect(Collectors.toList())

其中Collectors.toList()在每次使用时都会创建一个新的收集器。

这引出了一个问题:是否允许和建议做类似的事情:

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

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

someStream.collect(TO_LIST)

需要收藏家时。

由于收藏家是无国籍的,只是一系列功能和特征,我认为它应该有用,但是OTOH,Collectors.toList()在每次通话时都会创建一个新的CollectorImpl<>

重用收集器的缺点是什么?

6 个答案:

答案 0 :(得分:35)

我认为这更像是一个风格的问题,但我们可以提出一些想法:

  • 似乎常见练习使用这样的CONST收集器对象。从这个意义上讲:这样做可能会让一些读者感到惊讶,而令人惊讶的读者很少是一件好事。
  • 然后:很少有代码可以被复制&#34;周围(也许不应该避免代码重复);但仍然:指向 distinct 收集器对象可能会使您更难以重新分解或重新使用流构造。
  • 除此之外:你自己说过;收集器重复使用取决于 无状态实现。所以你要让自己依赖任何无状态的实现。可能不是问题;但也许要记住风险
  • 可能更重要:从表面上看,您的想法对于优化来说是一个很好的意思。但是好吧当你担心&#34;表现效果&#34;使用流,然后创建最终收集器的单个对象将不会切断它&#34;!

我的意思是:如果你担心&#34;浪费&#34;性能;您宁愿查看使用流来确定该流是否正在使用&#34;足够&#34;的每一行和任何代码行。首先证明使用流的对象。这些流来自相当一些开销!

长话短说:java社区尚未找到&#34;标准最佳实践&#34;对于溪流;因此,我(个人)此时的两分钱:更喜欢那些每个人&#34;正在使用 - 避免做自己的事情。特别是当它与表现有关时#34;。

答案 1 :(得分:30)

由于Collector基本上是四个函数和特征标志的容器,因此重用它没有问题,但也很少有任何优点,因为这种轻量级对象对内存管理的影响可以忽略不计,如果没有完全被优化器删除。

与内置Collector一样,不重用Collectors的主要原因是,您无法以类型安全的方式执行此操作。为任意类型List提供收集器时,您需要取消选中操作以始终分发相同的Collector实例。如果您将Collector存储在正确类型的变量中,而不使用未经检查的操作,则只能将其用于List的一种类型,以保留该示例。

对于Collections.emptyList()等,JRE开发人员采用了不同的方式,但常量EMPTY_LISTEMPTY_MAPEMPTY_SET已经存在于引入之前泛型,我会说它们比少数可缓存的Collectors更通用,这是其他三十多个内置收集器中的四个特殊情况,由于它们的函数参数而无法缓存。由于函数参数通常是通过lambda表达式实现的,这些表达式生成未指定标识/相等的对象,因此将它们映射到收集器实例的缓存将具有不可预测的效率,但很可能远低于内存管理器处理临时实例的效率。

答案 2 :(得分:13)

图书馆提供工厂方法以获取有用的对象是一种很好的做法。由于库提供了这样的方法:Collectors.toList()让库决定是否每次创建一个新实例都是一个好习惯请求与否,而不是篡改库,从而降低可读性,并在实施更改时冒未来的问题。

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

答案 3 :(得分:6)

只是一个小小的注释,@ Holger在他的回答中说优化器是聪明的并且完全取代该构造是完全可行的,它被称为scalar replacement。当方法中使用的对象解构且其字段为stack allocated like normal local variables时。因此,结果Collector可能不会在JVM级别被视为对象本身。这将发生在JIT time

答案 4 :(得分:3)

使用单个静态对象代替即时创建的经典问题是可变性。快速扫描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);
}

所以恕我直言 - 不要!

答案 5 :(得分:1)

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