Scala 不可变的val与可变的ArrayBuffer

Scala 不可变的val与可变的ArrayBuffer,scala,scala-collections,Scala,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

在我发布这个问题之前,我已经阅读了上面的文章。显然,如果您在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改变的唯一原因是
valb
将内存地址保存到
ArrayBuffer(1,2,3)

如果您尝试
var x=1;val y=x;x=5;y
,则输出仍然为1。在这种情况下,y将实际值而不是地址存储到x

Java没有这种混淆,因为很明显,对象不能被分配给int变量

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

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

Scala始终在JVM上运行(.NET支持已中断),因此在JVM上属于基本类型的类型将被Scala视为基本类型

使用val存储可变数组缓冲区有什么用


最接近的替代方法是使用
var
存储不可变的Seq。如果该Seq非常大,那么您不希望每次对其进行更改时都复制整个Seq,但这正是您可能需要做的!那太慢了

一个简单的答案是VAL和VAR都是参考值。Scala中没有基元类型。它们都是物体

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

val value = 5
val x = value
var y = value
您可以执行
y+=10
此操作将
y
更改为引用新对象
(5+10)
=
15
。原来的
5
仍为5

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

你可能想知道为什么你可以做
valb=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

这将确认
xs
ys
指向同一(可变)阵列缓冲区。
eq
方法类似于Java的
==
,它比较对象标识。可变/不可变引用(val/var)和可变/不可变数据结构(ArrayBuffer,List)是不同的。因此,如果您执行另一个
xs+=888
ys
是指向可变数据结构的不可变引用,它也包含
888
,不可变对象和常量值是两个不同的东西

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

将可变集合存储在不可变变量中有什么意义

val a=新阵列缓冲区(1)
a=新阵列缓冲[Int]()
:9:错误:重新分配到val

它防止变量被分配到新的内存地址。实际上,尽管scala鼓励您不要使用可变状态(以避免锁定、阻塞等),但我很难给出一个实际情况的示例,其中可变状态的var或val的选择很重要。

也请阅读,希望它能澄清本主题中的一些问题。一个问题:这是什么“xs.type”??要将scala中的每个数字都作为对象来实现有点困难,但是Java也有一个用于所有原语LOL的“Box”类。@windleiner,
xs.type
实际上是一种变量
xs
。你能举个“创建副本”的例子吗?它是否意味着像
valx=Array[Int](1,2)这样的构造;val y=(x+=5)
将起作用,因为您“创建一个副本
y
”而不是在x上工作?
val s=Seq(1);vals1=s++Seq(2);println(s);println(s1)。如您所见,
s
s1
是两个不同的实例。
val a = new ArrayBuffer(1)
a = new ArrayBuffer[Int]()
<console>:9: error: reassignment to val