我的代码中有很多地方可以:
someStream.collect(Collectors.toList())
其中Collectors.toList()
在每次使用时都会创建一个新的收集器。
这引出了一个问题:是否允许和建议做类似的事情:
private final static Collector<…> TO_LIST = Collectors.toList()
对于我使用的每种类型,然后使用单个收集器,如:
someStream.collect(TO_LIST)
需要收藏家时。
由于收藏家是无国籍的,只是一系列功能和特征,我认为它应该有用,但是OTOH,Collectors.toList()
在每次通话时都会创建一个新的CollectorImpl<>
。
重用收集器的缺点是什么?
答案 0 :(得分:35)
我认为这更像是一个风格的问题,但我们可以提出一些想法:
我的意思是:如果你担心&#34;浪费&#34;性能;您宁愿查看使用流来确定该流是否正在使用&#34;足够&#34;的每一行和任何代码行。首先证明使用流的对象。这些流来自相当一些开销!
长话短说:java社区尚未找到&#34;标准最佳实践&#34;对于溪流;因此,我(个人)此时的两分钱:更喜欢那些每个人&#34;正在使用 - 避免做自己的事情。特别是当它与表现有关时#34;。
答案 1 :(得分:30)
由于Collector
基本上是四个函数和特征标志的容器,因此重用它没有问题,但也很少有任何优点,因为这种轻量级对象对内存管理的影响可以忽略不计,如果没有完全被优化器删除。
与内置Collector
一样,不重用Collectors
的主要原因是,您无法以类型安全的方式执行此操作。为任意类型List
提供收集器时,您需要取消选中操作以始终分发相同的Collector
实例。如果您将Collector
存储在正确类型的变量中,而不使用未经检查的操作,则只能将其用于List
的一种类型,以保留该示例。
对于Collections.emptyList()
等,JRE开发人员采用了不同的方式,但常量EMPTY_LIST
,EMPTY_MAP
,EMPTY_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的物体。考虑到这些数字,整个练习变得毫无意义。