如何在Scala中复制一个列表

18

我想在Scala中进行浅拷贝。

我想要做的事情类似于:

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

但是clone方法是受保护的。


这与 https://dev59.com/onM_5IYBdhLWcg3wslbs 有关吗? - VonC
12
为什么?列表对象是不可变的。 - Alexander Azarov
1
我想在循环中从列表中删除一个元素(使用remove(A=>Boolean)方法)。(注:我两个小时前开始学习Scala :)) - Maxime
9
调用 myList.remove(s => s startsWith "f") 会创建一个新的列表,其中将删除 foomyList 保持不变。正如 Alexander Azarov 所说,Scala 中默认情况下列表是不可变的。 - Flaviu Cipcigan
3个回答

19

这里是一个无关的回答:不要这样做。由于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))
所有这些对象的重要一点是它们无法更改。它们没有任何setter,也没有任何方法可以更改它们的属性。它们将始终具有相同的值/引用作为它们的“头”(这就是我们称之为第一个元素的原因),并且具有相同的引用作为它们的“尾”(这就是我们称之为第二个元素的原因)。
但是,有一些方法看起来像是在改变列表。请放心,它们实际上是创建了新的列表。例如:
val l3 = list map (n => n + 1)

方法map创建一个全新的列表,大小与list相同,在这个列表中,可以从相应的元素计算出新元素(但你也可以忽略旧元素)。

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

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

val l5 = list.tail

这并不会创建一个新的列表。相反,它仅仅是将list中已经存在的一个元素赋值给了l5

val l6 = list drop 2

再次强调,没有创建新的列表。

val l7 = list take 1

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

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

  • List是一个抽象类。它有两个子类,分别是类::(是的,这就是类的名称)和单例对象NilList是sealed的,所以您不能添加新的子类,而::是final的,所以您也不能将其作为子类。

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


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)

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

4
用户要求复制一个列表(我猜是字符串列表,根据例子),你的回答是创建一个新列表来过滤整数列表中的奇数元素。为什么这个回答被接受为最佳答案?我有所遗漏吗? - Pablorotten
当问题要求克隆时,你如何回答过滤列表的相关问题? - Ayush Vatsyayan

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接