是否允许/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_LIST
, EMPTY_MAP
, EMPTY_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 variables
被stack 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的对象。 考虑到这些数字,整个练习变得毫无意义。