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

glg*_*lgl 44 java collections java-8 java-stream

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

someStream.collect(Collectors.toList())
Run Code Online (Sandbox Code Playgroud)

其中,Collectors.toList()创建于每次使用一个新的收藏家.

这引出了一个问题:是否允许和建议做以下事情:

private final static Collector<…> TO_LIST = Collectors.toList()
Run Code Online (Sandbox Code Playgroud)

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

someStream.collect(TO_LIST)
Run Code Online (Sandbox Code Playgroud)

当需要收藏家时

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

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

Gho*_*ica 34

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

  • 这似乎是常见的做法是使用这样一个const对象收藏家.从这个意义上说:这样做可能会让一些读者感到惊讶,而令人惊讶的读者很少是一件好事.
  • 然后:很少有代码可以被"复制"(并且可能不应该避免代码重复); 但仍然:指向一个独特的收集器对象可能会让你更难以重新分解或重新使用你的流构造.
  • 除此之外:你自己说过; 收集再利用依赖于一个无状态的实现.所以你要让自己依赖任何无状态的实现.可能不是问题; 但也许存在风险!
  • 可能更重要的是:从表面上看,您的想法看起来很适合优化.但是好吧 当你担心使用流的"性能影响"时,最后一个收集器的单个对象创建将"不会削减它"!

我的意思是:如果你担心"浪费"表现; 您宁愿查看使用流的每一行代码,以确定该流是否正在使用"足够"的对象来证明流的使用是合理的.这些流有很多开销!

长话短说:java社区尚未找到流的"标准最佳实践"; 因此,我(个人)此时的两分钱:更喜欢"每个人"正在使用的模式 - 避免做自己的事情.特别是当它与"性能相关"时.

  • 第三点,取决于无状态,即可共享的实现,不是问题,因为这是在实践中已经发生的事情,例如当你将`Collector`作为下游收集器传递给[`Collectors.groupingBy(Function,Collector)`]时(https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#groupingBy-java.util.function.Function-java.util.stream.Collector-),其中将为每个组重用收集器."收集器"返回的函数的各个合同也暗示了这一点. (7认同)

Hol*_*ger 29

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

Collector内置所示Collectors,不重用s的主要原因是,您无法以类型安全的方式执行此操作.当为任意类型的Lists 提供收集器时,您将需要未经检查的操作来始终分发相同的Collector实例.如果您Collector在一个正确类型的变量中存储,要在没有未经检查的操作的情况下使用,您只能将它用于一种类型的Lists,以保留该示例.

在的情况下Collections.emptyList(),等等,JRE开发人员去不同的方式,但常数EMPTY_LIST,EMPTY_MAP,EMPTY_SET已经引入泛型之前就已经存在,而我说,他们是不是少数缓存更灵活Collectors,这都只是四个特殊情况下进行其他超过30个内置收集器,由于它们的功能参数而无法缓存.由于函数参数通常是通过lambda表达式实现的,这些表达式生成未指定标识/相等的对象,因此将它们映射到收集器实例的缓存将具有不可预测的效率,但很可能远低于内存管理器处理临时实例的效率.

  • 这是关于这个网站的一件好事:不时有这些非常有趣的问题; 提出这么多不同而有趣的答案! (5认同)

Hon*_*dek 13

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

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


Eug*_*ene 6

只是一个小小的注释,@霍尔格在他的回答中说优化器是聪明的并完全取代该构造是完全可行的,它被称为a scalar replacement.当一个方法中所使用的对象的去构造和它的字段stack allocated like normal local variables.因此,结果Collector可能不会在JVM级别被视为Object本身.那会发生在JIT time.