【问题标题】:How to copy a list in Scala如何在 Scala 中复制列表
【发布时间】:2009-09-18 16:32:43
【问题描述】:

我想在 Scala 中浅拷贝一个列表。

我想做一些类似的事情:

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

但是克隆方法是受保护的。

【问题讨论】:

  • 那不是和stackoverflow.com/questions/1267261/…有关吗?
  • 有什么用?列表对象是不可变的。
  • 我想在循环中从列表中删除一个元素(使用 remove(A=>Boolean) 方法)。 (注意:我在 2 小时前开始使用 Scala :))
  • 调用myList.remove(s => s startsWith "f") 时,会创建一个新列表,其中删除了foomyList 保持完整。正如 Alexander Azarov 所说,默认情况下,列表在 Scala 中是不可变的。

标签: list scala copy shallow-copy


【解决方案1】:

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

让我们考虑几个操作:

val list = List(1,2,3)
val l1 = 0 :: list
val l2 = "a" :: list

l1l2 都不会改变 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 创建一个大小相同的全新列表,其中新元素可以从 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 是一个抽象类。它有两个后代,类::(是的,这是类的名称)和单例对象NilList 是密封的,所以不能添加新的子类,:: 是最终的,所以不能子类化。

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

【讨论】:

    【解决方案2】:

    过滤列表:

    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)
    

    请注意,如上所述,列表是不可变的。也就是说这些操作并没有修改原来的列表,而是实际创建了一个新的列表。

    【讨论】:

    • 用户请求了一种复制列表的方法(根据示例,我想是一个字符串列表),您的答案是创建一个新列表来过滤整数列表的奇数元素。为什么这个回复被接受为最佳答案?我错过了什么?
    • 当问题要求克隆时,您如何过滤相关列表?
    【解决方案3】:

    使用:_*类型注解

    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: Constructor taking either Seq or varargs

    【讨论】:

      猜你喜欢
      • 2013-07-01
      • 2014-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-07
      • 1970-01-01
      • 2013-02-26
      相关资源
      最近更新 更多