如何在Scala中复制列表

时间:2009-09-18 16:32:44

标签: list scala copy shallow-copy

我想在Scala中浅层复制一个列表。

我想做像:

val myList = List("foo", "bar")
val myListCopy = myList.clone

但克隆方法受到保护。

2 个答案:

答案 0 :(得分:14)

这是一个非答案:不要这样做。 List是不可变的,因此复制一个绝对没有意义。

让我们考虑一些操作:

val list = List(1,2,3)
val l1 = 0 :: list
val l2 = "a" :: list

l1l2都不会改变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))

关于所有这些对象的重点是,它们无法改变。没有setter,也没有任何方法可以改变它们的任何属性。它们将永远在它们的“头部”(我们称之为第一个元素)中具有相同的值/引用,并且在它们的“尾部”中具有相同的引用(这就是我们称之为第二个元素)。

但是,有些方法看起来就像他们正在更改列表一样。但请放心,他们正在创建新的列表。例如:

val l3 = list map (n => n + 1)

方法地图创建一个相同大小的全新列表,其中可以从list中的相应元素计算新元素(但您也可以忽略旧元素)。

val l4 = l2 filter (n => n.isInstanceOf[Int])

虽然l4list具有相同的元素(但是类型不同),但它也是一个全新的列表。方法filter根据您传递的规则创建一个新列表,告诉它哪些元素进入,哪些元素不进入。如果 可以返回现有列表,它不会尝试进行优化。

val l5 = list.tail

这不会创建新列表。相反,它只是将l5的现有元素分配给list

val l6 = list drop 2

同样,没有创建新列表。

val l7 = list take 1

然而,这会创建一个新列表,正是因为它无法更改list的第一个元素,因此它的尾部指向Nil

以下是一些其他实施细节:

  • List是一个抽象类。它有两个后代,类::(是的,这是类的名称)和单例对象NilList已被密封,因此您无法向其中添加新的子类,::是最终的,因此您无法将其子类化。

  • 虽然您无法执行任何更改列表的操作,但在某些操作中它会在内部使用可变状态。这有助于提高性能,但它是本地化的,因此您编写的程序无法检测到它,或者受其影响。您可以根据需要传递列表,无论其他函数对它们执行什么操作,或者有多少线程同时使用它们。

答案 1 :(得分:6)

过滤列表:

val list = List(1,2,3,4,5)
//only evens
val evens = list.filter(e=>e%2 == 0)

println(list)
//--> List(1, 2, 3, 4, 5)

println(evens) 
//--> List(2, 4)

您还可以使用通配符保存几个字符:

val evens = list.filter(_%2==0)

请注意,如上所述,列表是不可变的。这意味着这些操作不会修改原始列表,但实际上会创建一个新列表。