4

看着

val sb = Seq.newBuilder[Int]
println(sb.getClass.getName)
sb += 1
sb += 2
val s = sb.result()
println(s.getClass.getName)

输出是

scala.collection.mutable.ListBuffer
scala.collection.immutable.$colon$colon

使用 Scala 2.10.1。

例如,我希望Seq.newBuilder返回一个VectorBuilderCanBuildFrom如果结果显式键入 a ,则由 返回Seq

def build[T, C <: Iterable[T]](x: T, y: T)
                              (implicit cbf: CanBuildFrom[Nothing, T, C]): C = {
  val b = cbf()
  println(b.getClass.getName)
  b += x
  b += y
  b.result()
}

val s: Seq[Int] = build(1, 2)
println(s.getClass.getName) // scala.collection.immutable.Vector

在这种情况下,构建器是 a VectorBuilder,结果的类是 a Vector

所以我明确地想要构建一个Seq,但结果是一个List需要更多 RAM,根据Scala 集合内存占用特性

那么为什么Seq.newBuilderreturn aListBuffer最终给出了 aList呢?

4

4 回答 4

8

Scala Collections API非常复杂,而且它的层次结构非常深入。每个级别都代表某种新的抽象。该Seq特征分为两个不同的子特征,它们为性能提供了不同的保证(参考):

  1. AnIndexedSeq提供元素的快速随机访问和快速长度操作。其中一个代表IndexedSeqVector.

  2. ALinearSeq仅通过 head 提供对第一个元素的快速访问,但也具有快速尾操作。其中一个代表LinearSeqList.

由于 a 的当前默认实现Seq是 a ListSeq.newBuilder因此将返回 a ListBuffer。但是,如果您想使用 a Vector,您可以使用Vector.newBuilder[T]IndexedSeq.newBuilder[T]

scala> scala.collection.immutable.IndexedSeq.newBuilder[Int]
res0: scala.collection.mutable.Builder[Int,scala.collection.immutable.IndexedSeq[Int]] = scala.collection.immutable.VectorBuilder@1fb10a9f

scala> scala.collection.immutable.Vector.newBuilder[Int]
res1: scala.collection.mutable.Builder[Int,scala.collection.immutable.Vector[Int]] = scala.collection.immutable.VectorBuilder@3efe9969
于 2013-07-19T15:39:20.040 回答
4

默认Seq实现是List

Seq(1, 2, 3)  // -> List(1, 2, 3)

...因此ListBuffer是正确的构建器。如果需要Vector,请使用Vector.newBuilderIndexedSeq.newBuilder

于 2013-07-19T15:49:08.670 回答
1

好吧,但你不会相信的。打开-Yinfer-debug你的CanBuildFrom反例,

[search] $line14.$read.$iw.$iw.build[scala.this.Int, Seq[scala.this.Int]](1, 2) with pt=generic.this.CanBuildFrom[scala.this.Nothing,scala.this.Int,Seq[scala.this.Int]] in module class $iw, eligible:
  fallbackStringCanBuildFrom: [T]=> generic.this.CanBuildFrom[String,T,immutable.this.IndexedSeq[T]]
[solve types] solving for T in ?T
inferExprInstance {
  tree      scala.this.Predef.fallbackStringCanBuildFrom[T]
  tree.tpe  generic.this.CanBuildFrom[String,T,immutable.this.IndexedSeq[T]]
  tparams   type T
  pt        generic.this.CanBuildFrom[scala.this.Nothing,scala.this.Int,Seq[scala.this.Int]]
  targs     scala.this.Int
  tvars     =?scala.this.Int
}
[search] considering no tparams (pt contains no tvars) trying generic.this.CanBuildFrom[String,scala.this.Int,immutable.this.IndexedSeq[scala.this.Int]] against pt=generic.this.CanBuildFrom[scala.this.Nothing,scala.this.Int,Seq[scala.this.Int]]
[success] found SearchResult(scala.this.Predef.fallbackStringCanBuildFrom[scala.this.Int], ) for pt generic.this.CanBuildFrom[scala.this.Nothing,scala.this.Int,Seq[scala.this.Int]]
[infer implicit] inferred SearchResult(scala.this.Predef.fallbackStringCanBuildFrom[scala.this.Int], )

事实上,

  implicit def fallbackStringCanBuildFrom[T]: CanBuildFrom[String, T, immutable.IndexedSeq[T]] =
    new CanBuildFrom[String, T, immutable.IndexedSeq[T]] {
      def apply(from: String) = immutable.IndexedSeq.newBuilder[T]
      def apply() = immutable.IndexedSeq.newBuilder[T]
    }

你是什​​么意思,你的 Iterable 不是字符串?

trait CanBuildFrom[-From, -Elem, +To]

这就是推断 Nothing 或 Any 的坏处。

编辑:对不起,我说错了,我看到你没有明确地告诉它。

更新:

由于 CBF 在 中是逆变的From,因此来自 的 CBFString充当来自 的 CBF Nothing

scala> typeOf[CanBuildFrom[Nothing,Int,Seq[Int]]] <:< typeOf[CanBuildFrom[String,Int,Seq[Int]]]
res0: Boolean = false

scala> typeOf[CanBuildFrom[String,Int,Seq[Int]]] <:< typeOf[CanBuildFrom[Nothing,Int,Seq[Int]]]
res1: Boolean = true

例如,如果您需要从 构建immutable.Map,您需要一个 CBF fromcollection.Map来工作。

正如其他人评论的那样,这对于Nothing. 但是你得到了你所要求的。也就是说,您未指定,这意味着您不太介意返回的内容,Vector 或其他内容。

于 2013-07-20T00:14:54.807 回答
0

我同意这很奇怪。Vector.newBuilder如果这就是你要找的东西,你为什么不直接使用呢?

scala> val sb = Vector.newBuilder[Int]
sb: scala.collection.mutable.Builder[Int,scala.collection.immutable.Vector[Int]] = scala.collection.immutable.VectorBuilder@1fb7482a

scala> println(sb.getClass.getName)
scala.collection.immutable.VectorBuilder

scala> sb += 1
res1: sb.type = scala.collection.immutable.VectorBuilder@1fb7482a

scala> sb += 2
res2: sb.type = scala.collection.immutable.VectorBuilder@1fb7482a

scala> val s = sb.result()
s: scala.collection.immutable.Vector[Int] = Vector(1, 2)

scala> println(s.getClass.getName)
scala.collection.immutable.Vector
于 2013-07-19T15:33:56.937 回答