Java流的性能。concat VS Collection。阿道尔
用于在一个流中组合两组数据
Stream.concat(stream1, stream2).collect(Collectors.toSet());
或
stream1.collect(Collectors.toSet())
.addAll(stream2.collect(Collectors.toSet()));
哪个更有效?为什么
你可以在下面搜索框中键入要查询的问题!
用于在一个流中组合两组数据
Stream.concat(stream1, stream2).collect(Collectors.toSet());
或
stream1.collect(Collectors.toSet())
.addAll(stream2.collect(Collectors.toSet()));
哪个更有效?为什么
# 1 楼答案
在没有基准测试的情况下,不可能预先判断,但请考虑一下:如果有许多重复项,那么
Stream.concat(stream1, stream2)
必须创建一个大型对象,该对象必须实例化,因为您正在调用.collect()
然后
.toSet()
必须将每个事件与之前的每个事件进行比较,可能使用快速哈希函数,但仍然可能有很多元素另一方面,
stream1.collect(Collectors.toSet()) .addAll(stream2.collect(Collectors.toSet()))
将创建两个较小的集合,然后合并它们第二个选项的内存占用可能小于第一个选项
编辑:
在阅读了@NoDataFound benchmark之后,我重新审视了这个问题。在更复杂的测试版本上,确实是流。concat似乎在该系列中表现得更快。阿道尔。我试图考虑有多少不同的元素,以及初始流有多大。我还计算了从集合中创建输入流所需的时间(无论如何,这是可以忽略的)。下面是我使用下面代码获得的时间示例
密码
# 2 楼答案
出于可读性和意图的考虑,
Stream.concat(a, b).collect(toSet())
比第二种选择更加清晰为了回答这个问题,“什么是最有效的”,这里是一个JMH测试(我想说的是,我没有太多地使用JMH,可能还有一些改进我的基准测试的空间):
使用JMH,使用以下代码:
你会得到这些结果(为了可读性,我省略了一些部分)
使用
Stream.concat(a, b).collect(toSet())
的版本应该执行得更快(如果我能很好地阅读JMH数字)另一方面,我认为这个结果是正常的,因为你没有创建一个中间集(即使使用
HashSet
,这也有一些成本),正如第一个答案的评论所说,Stream
是延迟连接的使用探查器,你可能会看到哪一部分速度较慢。您可能还希望使用
toCollection(() -> new HashSet(1000))
而不是toSet()
来查看问题是否在于增加HashSet
内部哈希数组# 3 楼答案
两者都可以
如果你对你的应用程序进行了配置,这段代码是一个瓶颈,那么考虑用不同的实现来分析你的应用程序,并使用一个最有效的
# 4 楼答案
你的问题被称为premature optimization。不要仅仅因为你认为一种语法更快就选择另一种语法。始终使用最能表达意图并支持理解逻辑的语法
没错
但我不需要
通常有两种情况:
您开发了一个OLTP应用程序。在这种情况下,应用程序应该在一秒钟或更短的时间内响应。用户不会体验到您提供的变体之间的性能差异
你开发了一种batch processing可以在无人看管的情况下运行一段时间。在这种情况下,性能差异“可能”很重要,但前提是要按批处理运行的时间收费
不管怎样: 真正的性能问题(应用程序的速度是成倍的,而不是分数)通常是由实现的逻辑引起的(例如:过度的通信、“隐藏循环”或过度的对象创建)
这些问题通常无法通过选择特定的语法来解决或预防
如果为了提高性能而忽略可读性,则会使应用程序更难维护
而且,更改难以维护的代码库很容易消耗大量资金,这是因为程序在应用程序的生命周期中使用了可读性较差但速度稍快的语法,从而提高了速度
毫无疑问,人们很好奇
如果你知道,你为什么要问
请您将测量结果和测量装置一起分享,好吗
更重要的是:这对Java9或Java10有效吗
Java的性能基本上来自JVM实现,这可能会发生变化。当然,对于较新的语法结构(如java流),新的java版本更有可能带来性能提升。但不能保证
5年后你还会负责这份申请吗? 或者你是一名顾问,在开始一个项目后,你会被支付报酬,然后转到下一个项目吗
我从来没有一个项目可以在语法层面解决我的性能问题
但我经常使用存在10多年的遗留代码,这很难维护,因为有人不尊重可读性
这是一个自由的世界,随便你选吧
# 5 楼答案
我当时正处于决定是否使用Stream的境地。of()与flatMap()或Stream。concat()或集合。addAll()或集合。add()将多个列表合并为单个列表。我对我的代码进行了10次迭代的快速测试,得到了一些令人惊讶的结果
这是我的密码
# 6 楼答案
首先,必须强调的是,第二种变体是不正确的。
toSet()
收集器返回带有“no guarantees on the type, mutability, serializability, or thread-safety”的Set
。如果不保证可变性,那么对结果Set
调用addAll
是不正确的它恰好与参考实现的当前版本一起工作,其中将创建一个
HashSet
,但可能在未来版本或替代实现中停止工作。为了解决这个问题,必须将第一个流的collect
操作的toSet()
替换为toCollection(HashSet::new)
这导致了第二个变体不仅在当前实现中效率较低的情况,如this answer所示,它还可能阻止将来对
toSet()
收集器进行优化,因为它坚持要求结果为HashSet
类型。此外,与toSet()
收集器不同,toCollection(…)
收集器无法检测目标集合是否无序,这在未来的实现中可能具有性能相关性