如何在Scala中复制列表

Max*_*ime 12 scala copy list shallow-copy

我想浅谈Scala中的列表.

我想做像:

val myList = List("foo", "bar")
val myListCopy = myList.clone
Run Code Online (Sandbox Code Playgroud)

但克隆方法受到保护.

Dan*_*ral 14

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

让我们考虑一些操作:

val list = List(1,2,3)
val l1 = 0 :: list
val l2 = "a" :: list
Run Code Online (Sandbox Code Playgroud)

既不改变l1也不l2改变list,但它们都创建了引用的新列表list.

我们来详细解释一下.构造函数List(1,2,3)创建了三个元素,并使用单例对象.具体来说,它是实例化这些元素:

::(3, Nil)
::(2, reference to the previous element)
::(1, reference to the previous element)
Run Code Online (Sandbox Code Playgroud)

并且Nil是一个单身对象.标识符list实际指向的是最后一个元素.

现在,当您分配0 :: listl1,您正在实例化一个新对象:

::(0, reference to ::(1, etc))
Run Code Online (Sandbox Code Playgroud)

当然,既然有一个参考list,你可以把它想象l1成四个元素的列表(如果你算的话,可以算是五个元素Nil).

现在l2甚至不是同一类型list,但它也引用它!这里:

::("a", reference to ::(1, etc))
Run Code Online (Sandbox Code Playgroud)

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

但是,有些方法看起来正在改变列表.但请放心,他们正在创建列表.例如:

val l3 = list map (n => n + 1)
Run Code Online (Sandbox Code Playgroud)

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

val l4 = l2 filter (n => n.isInstanceOf[Int])
Run Code Online (Sandbox Code Playgroud)

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

val l5 = list.tail
Run Code Online (Sandbox Code Playgroud)

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

val l6 = list drop 2
Run Code Online (Sandbox Code Playgroud)

同样,没有创建新列表.

val l7 = list take 1
Run Code Online (Sandbox Code Playgroud)

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

这里有一些额外的实现细节:

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

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


小智 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)
Run Code Online (Sandbox Code Playgroud)

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

val evens = list.filter(_%2==0)
Run Code Online (Sandbox Code Playgroud)

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

  • 用户请求一种复制列表的方法(根据示例,我认为是字符串列表),您的答案是创建一个新列表来过滤 Integer 列表的奇数元素。为什么这个回答被接受为最佳答案?我错过了什么? (4认同)