我想在Scala中浅层复制一个列表。
我想做像:
val myList = List("foo", "bar")
val myListCopy = myList.clone
但克隆方法受到保护。
答案 0 :(得分:14)
这是一个非答案:不要这样做。 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)
方法地图创建一个相同大小的全新列表,其中可以从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
是一个抽象类。它有两个后代,类::
(是的,这是类的名称)和单例对象Nil
。 List
已被密封,因此您无法向其中添加新的子类,::
是最终的,因此您无法将其子类化。
虽然您无法执行任何更改列表的操作,但在某些操作中它会在内部使用可变状态。这有助于提高性能,但它是本地化的,因此您编写的程序无法检测到它,或者受其影响。您可以根据需要传递列表,无论其他函数对它们执行什么操作,或者有多少线程同时使用它们。
答案 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)
请注意,如上所述,列表是不可变的。这意味着这些操作不会修改原始列表,但实际上会创建一个新列表。