我想在 Scala 中浅拷贝一个列表。
我想做一些类似的事情:
val myList = List("foo", "bar")
val myListCopy = myList.clone
但是克隆方法是受保护的。
我想在 Scala 中浅拷贝一个列表。
我想做一些类似的事情:
val myList = List("foo", "bar")
val myListCopy = myList.clone
但是克隆方法是受保护的。
这是一个非答案:不要那样做。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
是一个抽象类。它有两个后代,类::
(是的,这是类的名称)和单例对象Nil
。List
是密封的,所以你不能向它添加新的子类,并且::
是最终的,所以你不能对它进行子类化。
虽然您无法更改列表,但它在某些操作中内部使用可变状态。这有助于提高性能,但它是本地化的,因此您编写的任何程序都无法检测到它,或遭受它的后果。您可以随意传递列表,无论其他函数如何处理它们,或者有多少线程同时使用它们。
要过滤列表:
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)
请注意,如上所述,列表是不可变的。这意味着这些操作不会修改原始列表,而是实际创建一个新列表。
使用:_ * 类型注解
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