immutable val vs mutable ArrayBuffer

时间:2013-11-16 15:55:42

标签: scala scala-collections

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中的变量何时携带值,何时是内存地址?将可变集合存储在不可变变量中有什么意义?

4 个答案:

答案 0 :(得分:4)

一个简单的答案是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。+ =(某事)。方法+=只是向其可变self中添加一个新元素(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

答案 1 :(得分:3)

  

将可变集合存储在不可变变量

中有什么意义
val a = new ArrayBuffer(1)
a = new ArrayBuffer[Int]()
<console>:9: error: reassignment to val

它可以防止将变量分配给新的内存地址。在实践中虽然scala鼓励你不要使用可变状态(以避免锁定,阻塞等),所以我很难找到一个真实情况的例子,其中可变状态选择var或val很重要。

答案 2 :(得分:2)

不可变对象和常量值是两回事。

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

答案 3 :(得分:0)

  

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

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

  

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

最接近的选择是使用var来存储不可变的Seq。如果Seq非常大,那么每次更改Seq时都不需要复制整个Seq - 但这就是你可能需要做的事情!那会非常慢!