我试图了解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
...
}
第一个问题是,我无法理解私有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 回答
::
可以是method或class . 在该示例中,它引用参数化类,它将新元素添加到List
的开头 .虽然
List
是不可变的,但var x
是可变的(ListBuffer
是可变的) . 它以空值开始,然后设置为新列表 .最后,
+=
方法将一个元素附加到列表和+=:
prepends,并且两者都是常量时间操作(与List
不同,它具有预先设置的常量时间和O(n ** 2)用于追加 .如果你看一下
::
的定义,你会看到传递给构造函数的tl
是私有var
.这意味着它只能在包
scala
(它所属的地方)内修改 . 并且ListBuffer
属于子包scala.collection.mutable
,因此可以访问tl
. 这是您在last1.tl
行中的else
块上看到的内容 .所以,关于附加一个新元素:
我们说
x = 42
现在,我们要添加追加
43
:"Programming in Scala"(the first edition is free online)的第22章解释了如何构造
List
以及如何使用ListBuffer
.