【问题标题】:immutable val vs mutable ArrayBuffer不可变 val 与可变 ArrayBuffer
【发布时间】:2013-11-29 23:46:35
【问题描述】:

mutable vs. immutable in Scala collections

在我发布这个问题之前,我已经阅读了上面的文章。显然如果你在 val 中存储一些东西,你不能修改它,但是如果你存储一个可变集合,例如 ArrayBuffer,你可以修改它!

scala> val b = ArrayBuffer[Int](1,2,3)
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)

scala> b += 1
res50: b.type = ArrayBuffer(1, 2, 3, 1)

scala> b
res51: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 1)

使用val 存储可变的ArrayBuffer 有什么用?我认为 b 更改的唯一原因是因为 val b 将内存地址保存到 ArrayBuffer(1,2,3)

如果你尝试var x = 1; val y = x; x = 5; y,输出仍然是1。在这种情况下,y 存储一个实际值而不是x 的地址。

Java 没有这种混淆,因为很明显不能将 Object 分配给 int 变量。

我如何知道 scala 中的变量何时携带值,何时是内存地址?将可变集合存储在不可变变量中有什么意义?

【问题讨论】:

标签: scala scala-collections


【解决方案1】:

我如何知道 scala 中的变量何时携带值,何时是内存地址?

Scala 始终在 JVM 上运行(.NET 支持已停止),因此 JVM 上的原始类型将被 Scala 视为原始类型。

使用 val 存储可变的 ArrayBuffer 有什么用?

最接近的替代方法是使用 var 来存储不可变的 Seq。如果该 Seq 非常大,您不会希望每次对其进行更改时都必须复制整个 Seq - 但这就是您可能必须做的!那会很慢!

【讨论】:

    【解决方案2】:

    一个简单的答案是 vals 和 vars 都是引用。 Scala 中没有原始类型。它们都是对象。

    val x = 1
    

    是一个名为x 的引用,它指向一个不可变的整数对象1。你不能做1.changeTo(2) 什么的,所以如果你有

    val value = 5
    val x = value
    var y = value
    

    您可以执行y += 10 这会将y 更改为引用一个新对象(5 + 10) = 15。原来的5 仍然是 5。

    另一方面,您不能使用x += 10,因为x 是一个val,这意味着它必须始终指向5。所以,这不会编译。

    您可能想知道为什么您可以先使用val b = ArrayBuffer(...),然后再使用b += something,即使b 是一个val。那是因为+= 实际上是一个方法,而不是一个赋值。调用 b += something 会被翻译成 b.+=(something)。 += 方法只是将一个新元素 (something) 添加到其可变自身并返回自身以进行进一步分配。

    我们来看一个例子

    scala> val xs = ArrayBuffer(1,2,3)
    xs: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
    
    scala> val ys = ( xs += 999 )
    ys: xs.type = ArrayBuffer(1, 2, 3, 999)
    
    scala> xs
    res0: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 999)
    
    scala> ys
    res1: xs.type = ArrayBuffer(1, 2, 3, 999)
    
    scala> xs eq ys
    res2: Boolean = true
    

    这确认xsys 指向同一个(可变)ArrayBuffer。 eq 方法类似于 Java 的 ==,它比较对象身份。可变/不可变引用(val/var)和可变/不可变数据结构(ArrayBuffer、List)是不同的。所以,如果你再做一个xs += 888ys 是一个指向可变数据结构的不可变引用也包含888

    【讨论】:

    • 一个问题:这个“xs.type”是什么?将 scala 中的每个数字都实现为对象有点困难,但 Java 也有一个适用于所有基元 LOL 的“Box”类。
    • @WindDweller, xs.type 实际上是一种变量xs
    【解决方案3】:

    不可变对象和常量值是两个不同的东西。

    如果您将您的集合定义为 val,则表示该集合的引用实例将始终相同。但是这个实例可以是可变的或不可变的:如果它是不可变的,则您不能在该实例中添加或删除项目,反之亦然,如果它是可变的,您可以这样做。当集合是不可变的以添加或删除项目时,您总是创建一个副本。

    【讨论】:

    • 你能举一个“创建副本”的例子吗?这是否意味着像 val x = Array[Int](1,2); val y = (x+=5) 这样的构造会起作用,因为您“创建了一个副本 y”而不是在 x 上工作?
    • val s = Seq(1); val s1 = s ++ Seq(2); println(s); println(s1);。如您所见,ss1 是两个不同的实例。
    【解决方案4】:

    将可变集合存储在不可变变量中的意义何在

    val a = new ArrayBuffer(1)
    a = new ArrayBuffer[Int]()
    <console>:9: error: reassignment to val
    

    它可以防止将变量分配给新的内存地址。在实践中,虽然 scala 鼓励您不要使用可变状态(以避免锁定、阻塞等),但我无法为可变状态选择 var 或 val 的实际情况提供一个示例。

    【讨论】:

      猜你喜欢
      • 2012-07-08
      • 2011-07-01
      • 1970-01-01
      • 2016-05-31
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      相关资源
      最近更新 更多