这是一个非答案:不要那样做。 List 是不可变的,所以复制一个绝对没有意义。
让我们考虑几个操作:
val list = List(1,2,3)
val l1 = 0 :: list
val l2 = "a" :: list
l1 和 l2 都不会改变 list,但它们都会创建引用 list 的新列表。
让我们详细解释一下。构造函数List(1,2,3) 正在创建三个元素,并且还使用了一个单例对象。具体来说,它正在实例化这些元素:
::(3, Nil)
::(2, reference to the previous element)
::(1, reference to the previous element)
而Nil 是一个单例对象。标识符list 实际指向的是最后一个元素。
现在,当您将0 :: list 分配给l1 时,您正在实例化一个新对象:
::(0, reference to ::(1, etc))
当然,由于引用了list,您可以将l1 视为四个元素的列表(如果算上Nil,则为五个)。
现在l2 甚至不是list 的同一类型,但它也引用了它!这里:
::("a", reference to ::(1, etc))
然而,关于所有这些对象的重要一点是它们无法更改。没有设置器,也没有任何方法会改变它们的任何属性。它们的“头部”(这就是我们所说的第一个元素)中将永远具有相同的值/引用,并且它们的“尾部”(这就是我们所谓的第二个元素)中的相同引用。
但是,有些方法看起来 像它们正在更改列表。不过请放心,他们正在创建新 列表。例如:
val l3 = list map (n => n + 1)
方法 map 创建一个大小相同的全新列表,其中新元素可以从 list 中的相应元素计算而来(但您也可以忽略旧元素)。
val l4 = l2 filter (n => n.isInstanceOf[Int])
虽然l4 具有与list 相同的元素(但类型不同),但它也是一个全新的列表。 filter 方法创建一个新列表,根据您传递的规则告诉它哪些元素进入,哪些不进入。它不会尝试优化以防它可以返回现有列表。
val l5 = list.tail
这不会创建新列表。相反,它只是将list 的现有元素分配给l5。
val l6 = list drop 2
同样,没有创建新列表。
val l7 = list take 1
然而,这会创建一个新列表,正是因为它无法更改list 的第一个元素,使其尾部指向Nil。
这里有一些额外的实现细节: