16

我想在 Scala 中浅拷贝一个列表。

我想做一些类似的事情:

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

但是克隆方法是受保护的。

4

3 回答 3

18

这是一个非答案:不要那样做。AList是不可变的,所以复制一个绝对没有意义。

让我们考虑一些操作:

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 方法创建了一个全新的列表,大小相同,其中新元素可以从 in 的相应元素中计算出来list(但您也可以忽略旧元素)。

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

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

val l5 = list.tail

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

val l6 = list drop 2

同样,没有创建新列表。

val l7 = list take 1

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

以下是一些额外的实现细节:

  • List是一个抽象类。它有两个后代,类::(是的,这是类的名称)和单例对象NilList是密封的,所以你不能向它添加新的子类,并且::是最终的,所以你不能对它进行子类化。

  • 虽然您无法更改列表,但它在某些操作中内部使用可变状态。这有助于提高性能,但它是本地化的,因此您编写的任何程序都无法检测到它,或遭受它的后果。您可以随意传递列表,无论其他函数如何处理它们,或者有多少线程同时使用它们。

于 2009-09-18T22:33:24.767 回答
5

要过滤列表:

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)

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

于 2009-09-18T18:42:58.360 回答
0

使用:_ * 类型注解

scala> val l1 = List(1,2,3)
l1: List[Int] = List(1, 2, 3)

scala> val l2 = List(l1:_*)
l2: List[Int] = List(1, 2, 3)

有关注释的更多详细信息:Scala:构造函数采用 Seq 或 varargs

于 2021-09-30T06:39:37.897 回答