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中的变量何时携带值,何时是内存地址?将可变集合存储在不可变变量中有什么意义?
答案 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
这确认xs
和ys
指向相同(可变)的ArrayBuffer。 eq
方法类似于Java ==
,它比较对象标识。可变/不可变引用(val / var)和可变/不可变数据结构(ArrayBuffer,List)是不同的。因此,如果您执行另一个xs += 888
,ys
是指向可变数据结构的不可变引用,也包含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 - 但这就是你可能需要做的事情!那会非常慢!