一个关于Scala中ListBuffer类的+ =方法源代码的初学者谜题

时间:2016-06-23 06:45:33

标签: scala

ListBuffer可以有效地将元素附加到其末尾,然后转换为List

我研究了ListBuffer附加方法(+=)的源代码,但发现很难理解其工作原理。详细的源代码是here

摘录源代码:

final class ListBuffer[A] extends AbstractBuffer[A] {
  private var start: List[A] = Nil
  private var last0: ::[A] = _
  private var exported: Boolean = false
  private var len = 0

  def += (x: A): this.type = {
    if (exported) copy()
    if (isEmpty) {
      last0 = new :: (x, Nil)
      start = last0
    } else {
      val last1 = last0     // last1 is a local variable, is it necessary here?
      last0 = new :: (x, Nil)
      last1.tl = last0      // 
    }
    len += 1
    this
   }
}

else部分中,定义了局部变量last1,然后构造为在末尾包含多个元素。 last0始终指向最后一个单元格。

这里需要last1吗?毕竟,阻塞之后它将超出范围。我不明白为什么作者必须在这里定义last1

3 个答案:

答案 0 :(得分:2)

那么,

  val last1 = last0     // last1 is a local variable, is it necessary here?
  last0 = new :: (x, Nil)
  last1.tl = last0      // 

正如您所看到的,在我们已经更改.tl之后,它确实用于更改last0

那时.tl是什么?

@SerialVersionUID(509929039250432923L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
  override def tail : List[B] = tl
  override def isEmpty: Boolean = false
}

正如您所看到的,类::并不是真正不可变的:对列表的其余部分有一个可以更改的引用,这正是最后一行所做的。

现在为什么在更改last0后需要更改?那是因为我们需要一个临时变量来保留新创建的最后一个元素,我们需要将last0分配给该元素 - 所以我们只需重新分配last0并一次创建新的最后一个元素,并在之后链接旧的最后一个元素(在last1中保存)。

希望它有所帮助。

答案 1 :(得分:1)

这里发生的事情基本上是

last0.ti = new :: (x, Nil)  // put new element x at end of list

除了这种减少有问题:last0总是需要“指向”结束。因此last1成为对列表末尾的临时引用,以便可以访问end-of-the-list.t1并添加新的列表结尾。

答案 2 :(得分:0)

所以有3行。

last1将指向当前的last0。

last0设置为新的最后一个元素。

last1.t1设置为last0。

last1不是新对象。它只是指向当前的最后一个。 t1我假设是指向其下一个元素的类指针。 那么它的作用是将当前的最后一个元素指向新元素。

这在scala中可能会造成混淆,因为它使用了scala中可以避免的mutable和vars。