这里有很多性能建议,但可悲的是,其中大部分都是猜测,很少指向真正的性能考虑因素。
@Holger通过指出我们应该抵制看似压倒性的趋势来让性能尾随API设计狗而变得正确。
虽然在任何给定的情况下,有无数的考虑因素可以使流比其他形式的遍历更慢,相同或更快,但有一些因素表明流在大数据集上具有性能优势。
与在开始计算之前创建一个 -- 几个对象相比,创建 有一些额外的固定启动开销。如果您的数据集很大,则无关紧要;这是一个很小的启动成本,在大量的计算中摊销。(如果你的数据集很小,那可能也无关紧要 - 因为如果你的程序在小数据集上运行,性能通常也不是你最关心的问题。这确实很重要的地方是并行时;建立管道所花费的任何时间都会进入阿姆达尔定律的序列部分;如果你看一下实现,我们努力在流设置期间保持对象计数,但我很乐意找到减少它的方法,因为这对盈亏平衡数据集的大小有直接影响,其中并行开始赢得顺序。Stream
Iterator
但是,比固定启动成本更重要的是每个元素的访问成本。在这里,流实际上赢了 - 而且经常赢了大 - 有些人可能会感到惊讶。(在我们的性能测试中,我们经常看到流管道的性能可能优于其 for 循环。而且,对此有一个简单的解释:每个元素的访问成本从根本上低于 ,甚至按顺序。这有几个原因。Collection
Spliterator
Iterator
迭代器协议从根本上说效率较低。它需要调用两个方法来获取每个元素。此外,由于迭代器必须对诸如不带 或多次不调用之类的事情是健壮的,因此这两种方法通常都必须进行一些防御性编码(并且通常更具状态性和分支性),这增加了低效率。另一方面,即使是遍历分路器()的缓慢方式也没有这种负担。(对于并发数据结构来说,情况更糟,因为/对偶性从根本上来说是不规则的,并且实现必须做更多的工作来防御并发修改而不是实现。next()
hasNext()
hasNext()
next()
tryAdvance
next
hasNext
Iterator
Spliterator
Spliterator
进一步提供了一个“快速路径”迭代-- --可以在大多数时间使用(reduce,forEach),进一步减少了调解访问数据结构内部的迭代代码的开销。这也倾向于很好地内联,这反过来又提高了其他优化的有效性,例如代码运动,边界检查消除等。forEachRemaining
此外,遍历 via 的堆写入次数往往比 使用 的堆写入次数少得多。使用 ,每个元素都会导致一个或多个堆写入(除非可以通过转义分析将其字段提升到寄存器中来标量。除其他问题外,这会导致GC卡标记活动,从而导致卡标记的缓存行争用。另一方面,往往具有较少的状态,并且工业级实现倾向于将任何内容写入堆,直到遍历结束,而不是将其迭代状态存储在自然映射到寄存器的局部变量中,从而减少内存总线活动。Spliterator
Iterator
Iterator
Iterator
Spliterators
forEachRemaining
总结:别担心,要快乐。 是一个更好的,即使没有并行性。(它们通常也更容易写,更难出错。Spliterator
Iterator