Scala集合,引用和内存效率

时间:2014-09-07 09:52:25

标签: scala scala-2.10

Scala集合是否只能初始化/分配vals,vars和literals的字面值,而不是vals / vars本身?

即。下面的列表b将为(4, 3),并且无法从集合中引用a而不是在集合中托管其值?

val a = 3
val b = List(4, a)

我是否应该假设“完成”此类引用的唯一方法是从数据切换到对象,因为默认情况下主要引用对象?在性能很重要的情况下,使用对象可能不是非常有效的计算和内存。

关于纯粹的性能方面,假设a是一些大型集合而不仅仅是一个数字,当上述b初始化发生时,Scala会在内存中复制其“内容”吗?

谢谢!

2 个答案:

答案 0 :(得分:2)

当您调用List的构造函数时,会立即计算其参数(按值调用),因此对象3将存储在List中。

通常,scala中的所有内容都是一个对象(不考虑JVM表示细节),因此您存储的是3的不可变引用,它依次是不可变的。

另请注意 - 由于referencial transparencya是对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) 

不确定如何将关系表达为引用透明度 - 但它确实与原始问题相关