Scala集合是否只能初始化/分配vals,vars和literals的字面值,而不是vals / vars本身?
即。下面的列表b
将为(4, 3)
,并且无法从集合中引用a
而不是在集合中托管其值?
val a = 3
val b = List(4, a)
我是否应该假设“完成”此类引用的唯一方法是从数据切换到对象,因为默认情况下主要引用对象?在性能很重要的情况下,使用对象可能不是非常有效的计算和内存。
关于纯粹的性能方面,假设a
是一些大型集合而不仅仅是一个数字,当上述b
初始化发生时,Scala会在内存中复制其“内容”吗?
谢谢!
答案 0 :(得分:2)
当您调用List
的构造函数时,会立即计算其参数(按值调用),因此对象3
将存储在List
中。
通常,scala中的所有内容都是一个对象(不考虑JVM表示细节),因此您存储的是3
的不可变引用,它依次是不可变的。
另请注意 - 由于referencial transparency(a
是对3
的常量引用) - 存储对a
的引用或它引用的对象不对有所作为,即它是“透明的”。
因此,如果您想要对以后可以更改的内容进行不透明引用,则可以始终存储对可变对象的常量引用:
scala> class Foo(var foo: Int)
defined class Foo
scala> val x = new Foo(42)
x: Foo = Foo@3a654e77
scala> val a = List(x)
a: List[Foo] = List(Foo@3a654e77)
scala> a.head.foo
res25: Int = 42
scala> x.foo = 43
x.foo: Int = 43
scala> a.head.foo
res26: Int = 43
但男孩那是邪恶的!
根据效果问题,如果a
是一个较大的不可变集合,则参照透明度允许在构建a
时重用现有集合b
,悲观地复制它。由于a
不能变异,因此根本不需要克隆。
您可以在REPL中轻松测试:
让我们创建一个不可变的集合a
scala> val a = List(1, 2)
a: List[Int] = List(1, 2)
让我们使用a
创建b
scala> val b = List(a, List(3, 4))
b: List[List[Int]] = List(List(1, 2), List(3, 4))
b
的第一个元素完全与我们设置的a
相同
scala> b.head eq a
res18: Boolean = true
请注意,eq
会比较引用相等性,因此上述内容不仅仅是a
的副本。进一步证明:
scala> List(1, 2) eq a
res19: Boolean = false
答案 1 :(得分:0)
感谢@Gabriele Petronella的精彩回答。这不能作为评论格式化,所以我只是在这里添加它作为扩充。
我认为,除了引用之外,以下内容也非常有用,尽管eq
有点难以插入到此处。
scala> val a = MutableList(1,2)
a: scala.collection.mutable.MutableList[Int] = MutableList(1, 2)
scala> val b = List(a, 3, 4)
b: List[Any] = List(MutableList(1, 2), 3, 4)
scala> a += 100
res20: a.type = MutableList(1, 2, 100)
scala> b
res21: List[Any] = List(MutableList(1, 2, 100), 3, 4)
不确定如何将关系表达为引用透明度 - 但它确实与原始问题相关