当前位置: 首页 > 面试题库 >

迭代器与Java 8流

蒲寂离
2023-03-14
问题内容

为了利用java.util.streamJdk
8中包含的多种查询方法,我尝试设计一种域模型,其中具有*多重性(具有零个或多个实例)的关系的获取器返回a
Stream<T>而不是Iterable<T>or Iterator<T>

我的疑问是Stream<T>,与相比,是否还会产生任何额外的管理费用Iterator<T>

那么,用损害我的域模型是否有任何缺点Stream<T>

或者,我是否应该始终返回Iterator<T>Iterable<T>,并通过将迭代器与转换为最终结果,让最终用户选择是否使用流的决定StreamUtils

请注意 ,返回a Collection是无效的选项,因为在这种情况下,大多数关系都是惰性的并且大小未知。


问题答案:

这里有很多性能建议,但可悲的是,其中大部分是猜测,几乎没有指向真正的性能考虑因素。

@Holger通过指出我们应该抵制看似压倒性的趋势,让性能拖尾API设计狗,来正确地对待它。

尽管有成千上万的考虑因素可以使流在任何给定情况下都比其他形式的遍历慢,相同或比其他遍历慢,但有一些因素表明,流具有性能优势,这在很大程度上取决于数据集。

有一些额外的固定启动开销 创建
一个Stream比创造一个Iterator-你开始计算前几个对象。如果您的数据集很大,那就没关系;这是一笔很小的启动费用,需要大量计算来摊销。(如果你的数据集是小,这或许也并不重要-
因为如果你的程序在小数据集运行,性能一般不是你的#1关心无论是。)凡本 问题是何时并行
任何花费在建立管道上的时间都将成为阿姆达尔定律的系列部分;如果您看一下实现,我们会努力在流设置过程中减少对象计数,但是我很乐于找到减少它的方法,因为这直接影响了盈亏平衡的数据集大小,并行开始赢得竞争顺序的。

但是,比固定启动成本更重要的是每个元素的访问成本。在这里,信息流实际上是赢家,而且常常是赢家,有些人可能会感到惊讶。(在性能测试中,我们通常会看到流管道的性能优于对等的流循环Collection。)而且,对此有一个简单的解释:Spliterator从根本上说,每个元素的访问成本Iterator甚至比顺序都要低。有几个原因。

  1. 从根本上讲,迭代器协议的效率较低。它需要调用两个方法来获取每个元素。此外,由于迭代器必须对诸如next()不带hasNext()hasNext()不带多次调用之类的东西具有鲁棒性next(),因此这两种方法通常都必须进行防御性编码(通常具有更多的有状态性和分支性),这增加了效率。另一方面,即使使用慢速方式遍历分离器(tryAdvance)也没有此负担。(对于并发数据结构,甚至更糟,因为next/ hasNext对偶从根本上讲是不合理的,并且Iterator实现必须比防御实现更多地工作来防御并发修改Spliterator。)

  2. Spliterator进一步提供了“快速路径”迭代- forEachRemaining可以在大多数时间使用(缩减,forEach),从而进一步减少了中介代码访问数据结构内部的迭代代码的开销。这也倾向于很好地内联,这反过来又提高了其他优化的有效性,例如代码运动,边界检查消除等。

  3. 此外,遍历过的Spliterator趋向于堆写入的数量要少于with Iterator。使用Iterator,每个元素都会导致一个或多个堆写入(除非Iterator可以通过转义分析对其进行标量,并将其字段提升到寄存器中。)除其他问题外,这还会导致GC卡标记活动,从而导致卡标记的缓存行争用。另一方面,Spliterators倾向于具有较少的状态,而工业强度的forEachRemaining实现则倾向于将任何内容写入堆,直到遍历结束为止,而是将其迭代状态存储在自然映射到寄存器的本地中,从而减少了内存总线活动。

摘要:不用担心,要开心。 即使没有并行性也Spliterator更好Iterator。(它们通常也更容易编写,更难弄错。)



 类似资料:
  • 问题内容: 在一次采访中有人问我,使用迭代器使用for循环有什么好处,或者使用循环比迭代器有什么好处? 任何人都可以回答这个问题,以便将来如果我遇到类似的问题,那么我可以回答 问题答案: 首先,有两种for循环,它们的行为非常不同。一种使用索引: 这种循环并非总是可能的。例如,列表具有索引,而集合没有索引,因为它们是无序集合。 另一个foreach循环在幕后使用Iterator: 这适用于每种It

  • 本文向大家介绍Python迭代器与可迭代与生成器,包括了Python迭代器与可迭代与生成器的使用技巧和注意事项,需要的朋友参考一下 示例 一个迭代是一个对象,可以返回一个迭代器。具有状态且具有__iter__  方法并返回迭代器的任何对象都是可迭代的。也可能是没有状态的对象,该对象实现了__getitem__方法。-该方法可以获取索引(从零开始),并IndexError在索引不再有效时引发。 Py

  • 我一直在玩Java新的和闪亮的功能部分,最让我困惑的事情之一是流? 它们有什么用? 在谷歌上,我主要找到了如何使用它们的解释和实际例子,我已经记下来了,没有关于幕后魔法的具体内容,这是我感兴趣的。 我的意思并不是说,从实际意义上讲,我从一些函数语言中找到了map/filter/reduce/etc。相当快,但为什么我们需要先转换为流?Java已经有了迭代器。流和迭代器之间是否有根本的区别,比如一个

  • 是否可以从迭代器创建一个流,其中对象的序列与通过反复调用迭代器的next()方法生成的序列相同?我所考虑的具体情况涉及到Treeset.desceningIterator()返回的迭代器的使用,但是我可以想象在其他情况下,迭代器是可用的,而不是它所引用的集合。 例如,对于,我们可以编写并按照该集合的排序顺序获取该集合中的对象流,但是如果我们希望它们按照不同的顺序,比如通过使用获得的顺序呢?我想象的

  • 我最近开始检查新的Java8特性。 我遇到过这个迭代器-它对集合进行迭代。 比如,我希望它从第2个值开始,然后迭代到第2个最后一个值。或者类似的东西--或者其他元素。 我要怎么做?

  • JavaScript中有没有已知的技巧来区分和之间的区别,而不触发迭代? 我正在尝试实现以下类型检查器: 我知道调用会告诉我们这一点,但在无法触发迭代时我需要它。 此外,即使我在TypeScript中给出了示例,我也需要在运行时严格检查它。