为什么Seq.newBuilder返回ListBuffer?

时间:2013-07-19 14:45:28

标签: scala scala-collections

看着

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返回VectorBuilder例如。如果结果明确键入CanBuildFrom

,则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

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

所以我明确地希望构建一个Seq,但结果是List需要更多RAM,根据Scala collection memory footprint characteristics

那么为什么Seq.newBuilder会返回一个ListBuffer,最终给出List

4 个答案:

答案 0 :(得分:6)

Scala Collections API非常复杂,其层次结构非常丰富。每个级别代表某种新的抽象。 Seq特征分为两个不同的子特征,它们对性能提供不同的保证(ref.):

  1. IndexedSeq提供元素的快速随机访问和快速长度操作。这个IndexedSeq的一个代表是Vector

  2. LinearSeq仅通过head提供对第一个元素的快速访问,但也具有快速尾部操作。这个LinearSeq的一个代表是List

  3. 由于Seq的当前默认实施是ListSeq.newBuilder将返回ListBuffer。但是,如果您想使用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
    

答案 1 :(得分:3)

默认Seq实施是List

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

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

答案 2 :(得分: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不是String?

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

无论是Nothing还是Any,都是邪恶的。

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

更新

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

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进行构建,则需要collection.Map的CBF才能正常工作。

正如其他人评论的那样,Nothing只是奇怪。但是你得到了你所要求的。也就是说,你没有明确指出,这意味着你不太关心你得到什么,Vector或其他什么。

答案 3 :(得分: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