我想了解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等改变?
答案 0 :(得分:3)
::
可以是method或class。在示例中,它引用参数化类,它将新元素添加到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
。