我想在Scala中进行浅拷贝。
我想要做的事情类似于:
val myList = List("foo", "bar")
val myListCopy = myList.clone
但是clone方法是受保护的。
我想在Scala中进行浅拷贝。
我想要做的事情类似于:
val myList = List("foo", "bar")
val myListCopy = myList.clone
但是clone方法是受保护的。
这里是一个无关的回答:不要这样做。由于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])
虽然 l4
和 list
具有相同的元素(但类型不同),但它也是一个全新的列表。方法 filter
基于您传递的规则创建一个新列表,告诉它哪些元素进入列表,哪些不进入。它不会尝试优化以返回现有列表,即使它可以做到。
val l5 = list.tail
这并不会创建一个新的列表。相反,它仅仅是将list
中已经存在的一个元素赋值给了l5
。
val l6 = list drop 2
再次强调,没有创建新的列表。
val l7 = list take 1
然而,这会创建一个新的列表,因为它无法将list
的第一个元素更改为指向Nil
的尾部。
下面是一些额外的实现细节:
List
是一个抽象类。它有两个子类,分别是类::
(是的,这就是类的名称)和单例对象Nil
。 List
是sealed的,所以您不能添加新的子类,而::
是final的,所以您也不能将其作为子类。
虽然您无法更改列表,但在某些操作中它使用了内部的可变状态。这有助于提高性能,但是它是局部化的,因此您编写的任何程序都永远无法检测到它或从中受到后果。您可以随意传递列表,无论其他函数如何处理它们,或者有多少线程同时使用它们。
要筛选列表:
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
myList.remove(s => s startsWith "f")
会创建一个新的列表,其中将删除foo
。myList
保持不变。正如 Alexander Azarov 所说,Scala 中默认情况下列表是不可变的。 - Flaviu Cipcigan