语法:: [T] = _在Scala中意味着什么?

时间:2018-03-21 02:58:02

标签: scala

我想了解Listbuffer是如何实现的。

@SerialVersionUID(3419063961353022662L)
final class ListBuffer[A]
      extends AbstractBuffer[A]
         with Buffer[A]
         with GenericTraversableTemplate[A, ListBuffer]
         with BufferLike[A, ListBuffer[A]]
         with ReusableBuilder[A, List[A]]
         with SeqForwarder[A]
         with Serializable
{
  override def companion: GenericCompanion[ListBuffer] = ListBuffer

  import scala.collection.Traversable
  import scala.collection.immutable.ListSerializeEnd

  /** Expected invariants:
   *  If start.isEmpty, last0 == null
   *  If start.nonEmpty, last0 != null
   *  If len == 0, start.isEmpty
   *  If len > 0, start.nonEmpty
   */
  private var start: List[A] = Nil
  private var last0: ::[A] = _
  private var exported: Boolean = false
  private var len = 0
  ...
}

第一个问题是,我无法理解来自private var last0的语法 :: [A] = _ 。它看起来像一种类型,但它是什么意思:: [A]以及为什么它需要在RHS上?似乎语法用于表示List的最后一个元素,但它是如何工作的?

第二个问题与ListBuffer使用 Var 尾部而不是 Val 尾部(即使它是不可变的)以快速添加的事实有关。 上面的+ =函数如何帮助它实现更快的添加?

def += (x: A): this.type = {
  if (exported) copy()
  if (isEmpty) {
    last0 = new :: (x, Nil)
    start = last0
  } else {
    val last1 = last0
    last0 = new :: (x, Nil)
    last1.tl = last0
  }
  len += 1
 this
}

最后一个问题是ListBuffer的添加方式。似乎在空的ListBuffer填充第一个元素之后,start始终指向last0,并且start不会再次变为空。并且在第一个之后添加,将列表的尾部更改为由下面的代码行创建的新列表。

val last1 = last0 
last0 = new :: (x, Nil)
last1.tl = last0

看起来原始列表last0的尾部被更改为包含x的新last0,但似乎这段代码并没有真正扩展start指向的List。它是如何工作的..?似乎将last0分配给last1.tl会将List(x)附加到start指向的List,但我无法理解它是如何工作的。

如果可能的话,你能解释一下start,last0,last1将如何用输入序列+ = 1,+ = 2,+ = 3等改变?

1 个答案:

答案 0 :(得分:3)

::可以是methodclass。在示例中,它引用参数化类,它将新元素添加到List的开头。

scala> var x: ::[Int] = _
x: ::[Int] = null

scala> x = new ::[Int](1, List(2, 3))
x: ::[Int] = List(1, 2, 3)

虽然List是不可变的,但var x是可变的(ListBuffer是可变的)。它以空值开始,然后设置为新列表。

最后,+=方法将一个元素追加到列表中并+=:预先添加,两者都是常量时间操作(与List不同,它具有前置和O的恒定时间( n ** 2)追加。

如果您查看::的定义,您会看到传递给构造函数的tl是私有var

final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extends List[B] {
  override def head : B = hd
  override def tail : List[B] = tl
  override def isEmpty: Boolean = false

这意味着它只能在包scala(它所属的位置)内修改。 ListBuffer属于子包scala.collection.mutable,因此可以访问tl。这是您在第else行的last1.tl块中看到的内容。

所以,关于附加一个新元素:

我们说x = 42

// start is empty:
last0 = new ::(42, Nil) // List(42)
start = List(42)

现在,我们要添加追加43

// start is not empty
val last1 = List(42) // last1 is the same object as last0
last0 = new ::(43, Nil) // temporarily set last0 to List(43) //
last1.tl = List(43) // as last1 is the same as last0
                    // last0.tl = List(43)
                    // last0 points to the new last element
                    // start holds all elements in the buffer

“Scala编程”(the first edition is free online)的第22章解释了如何构造List以及如何使用ListBuffer