Scala:读取并保存Iterable的所有元素

时间:2011-07-21 20:29:57

标签: scala scala-collections

我有一个Iterable [T],它实际上是一个未知长度的流,并且想要全部读取它并将其保存为仍然是Iterable实例的东西。我真的必须阅读并保存它;我不能以懒惰的方式做到这一点。原始的Iterable至少可以有几千个元素。什么是最有效/最好/规范的方式?我应该使用ArrayBuffer,List,Vector吗?

假设xs是我的Iterable。我可以想到做这些可能性:

xs.toArray.toIterable     // Ugh?
xs.toList                 // Fast?
xs.copyToBuffer(anArrayBuffer)
Vector(xs: _*)            // There's no toVector, sadly. Is this construct as efficient?
编辑:我从问题中看到我应该更具体。这是一个稻草人的例子:

def f(xs: Iterable[SomeType]) {    // xs might a stream, though I can't be sure
    val allOfXS = <xs all read in at once>
    g(allOfXS)
    h(allOfXS)    // Both g() and h() take an Iterable[SomeType]
}

3 个答案:

答案 0 :(得分:4)

这很容易。几千个元素什么都不是,所以除非它是一个非常紧凑的循环,否则它几乎不重要。所以轻率的回答是:使用你认为最优雅的任何东西。

但是,好吧,让我们假设这实际上是在一个紧密的循环中,你可以预测或已经对你的代码进行基准测试,足以知道这是性能限制。

对于不可变解决方案,您的最佳性能可能是Vector,如下所示:

Vector() ++ xs

在我手中,这可以复制10k可迭代每秒约4k-5k次。 List大约是速度的一半。

如果你愿意尝试一个可变的解决方案,xs.toArray.toIterable通常会以每秒约10万份的速度拍摄。 ArrayBuffer的速度与List的速度大致相同。

如果您确实知道目标的大小(即sizeO(1)或者您从其他地方知道它),您可以通过仅分配来减少另外20-30%的执行速度正确的大小和写一个循环。

如果它实际上是原语,你可以通过编写你自己的专用Iterable之类的东西来获得因子10,它可以作用于数组并通过底层数组转换为常规集合。

底线:为了实现功能,速度和灵活性的完美结合,在大多数情况下使用Vector() ++ xsxs.toIndexedSeq默认为同一个东西,如果它已经是Vector,它将不会花费任何时间(并且很好地链接而不使用parens),并且你依赖于约定,而不是行为规范(并且需要输入1-3个字符)。

答案 1 :(得分:1)

Stream.force怎么样?

  

强制评估整个流并将其返回。

答案 2 :(得分:0)

这很难。 Iterable的方法是根据iterator来定义的,但是被子商品覆盖了。例如,IndexedSeq方法通常根据apply定义。

有一个问题是你为什么要复制 Iterable,但我想你可能会防止它变成可变的可能性。如果您不想复制它,那么您需要重新解释您的问题。

如果您要复制它,并且您希望确保以严格的方式复制所有元素,则可以使用.toList。这不会复制List,但不需要复制List。对于其他任何事情,它将产生一个新副本。