在我的应用程序中,我需要在集合中交换元素的能力。所以我有一个选择:可变变量与可变集合
- 使用可变集合声明为
val
- 或使用声明为
var
不可变集合(始终重新分配新的集合var
)
但在斯卡拉(函数式编程)总是可以避免可变性。那么更糟糕的是:使用val
声明的可变集合还是声明为var
的不可变集合?
在我的应用程序中,我需要在集合中交换元素的能力。所以我有一个选择:可变变量与可变集合
val
var
不可变集合(始终重新分配新的集合var
)但在斯卡拉(函数式编程)总是可以避免可变性。那么更糟糕的是:使用val
声明的可变集合还是声明为var
的不可变集合?
可变性是你最好保持在笼子里的野兽。理想情况下,在经过良好测试和高度使用类型的笼子像斯卡拉可变收藏是。
重新分配到var
可能很难读取/维护,特别是如果这发生在方法中的多个位置。
所以用val
,你至少得到一个不可变的参考。
一般而言,可变集合的范围应尽可能小。因此,在完全填充可变集合(如果存在短暂缓冲区的情况下)之后,可以将其变为不可变的集合,例如将其作为方法的结果返回。
由于dbyrne指出正确: 如果这是不可能的可变集合转换成不可变的一个(如果您正在实现例如高速缓存),以及可变集合是公共API的一部分,那么var
可能会更好。在这种情况下的另一个选项是在实现公共API的方法中创建一个不可变副本。
如果您使用持有不可变集合的var
,则可以相当自由地发布它(尽管您可能希望将var
标记为@volatile
)。在任何给定的时间,其他代码只能得到该状态的一个永不改变的特定快照。
如果使用持有可变集合实例的val
,则必须小心谨慎,因为它可以在更新时以不一致的状态进行目击。
你能分享一下关于@ volatile在Scala中的工作方式吗?从我的理解中可以看出,多个线程可能同时访问它,但我不确定这个注释是真的改变了还是对变量有影响?谢谢 – LaloInDublin
@volatile与java中的volatile关键字完全相同。它适用于VM级别。例如,请参阅http://stackoverflow.com/questions/2694439/how-does-volatile-actually-work。 –
这实际上取决于您是否需要广泛分享该集合。可变集合的优点是它通常比不可变集合更快,并且让单个对象传递更容易,而不必确保可以从不同的上下文中设置var。但是,因为它们可以从从下你改变,你必须要小心,即使在单线程上下文:
import collection.mutable.ArrayBuffer
val a = ArrayBuffer(1,2,3,4)
val i = a.iterator
i.hasNext // True
a.reduceToSize(0)
i.next // Boom!
java.lang.IndexOutOfBoundsException: 0
at scala.collection.mutable.ResizableArray$class.apply(ResizableArray.scala:43)
...
所以,如果它要得到广泛的应用,你应该考虑是否可以适当小心,以避免像这样的问题。将var
用于不可变的集合通常更安全;那么你可能会过时,但至少你不会因为段错而掉到你的脸上。
var v = Vector(1,2,3,4)
val i = v.iterator
i.hasNext // True
v = Vector[Int]()
i.next // 1
但是现在,你必须通过v
从可能修改(包含它的类以外,至少)任何方法的返回值。这也可能会造成问题,如果你忘了更新的原始值:
var v = Vector(1,2,3,4)
def timesTwo = v.map(_ * 2)
timesTwo
v // Wait, it's still 1,2,3,4?!
但后来这并不要么更新,它?:
a.map(_ * 2) // Map doesn't update, it produces a new copy!
所以,作为一般规则,
但你应该可能会违反此规定,因为您坚持遵守此规定。
我用一种安全的方法的工作原理是这样的。首先隐藏变种,使其线程安全的是这样的:
private[this] val myList: scala.collection.mutable.(whatever)
私人[这]限制变量不仅这一类,但只有这个确切的例子。没有其他变量可以访问它。
接下来,做一个辅助函数来返回它的一个不可改变的版本,只要外在的东西需要与它的工作:
def myListSafe = myList.toList (or some other safe conversion function like toMap, etc)
你可能会得到一些开销做这些转换,但它给你使用的灵活性一个安全的类内可变集合 - 同时提供机会在需要时将其导出为线程安全。正如你已经指出的另一种选择是不断mutate一个var指向一个不可变的集合。
与雷克斯达成一致,最大的担忧是你是否分享收藏品。在这种情况下使用不可变的。另一种选择:
@volatile
private var myList = immutable.List.empty[Foo]
def theList = myList
没有更多的上下文,它并没有真正有所作为。这两者都不是一种功能性方法。可变集合可能会比不可变var更好。 – dbyrne
@dbyrne *可变集合可能比不可变var * whoa更好?在同一支球队中不是那两个人吗?例如'val misterMutableCollection = collection.mutable ....' –
@ om-nom-nom对于不可变的变量,你必须构造一个全新的集合并将其交换。有了可变的val,你可以使用原始的集合和调用方法来改变它。 – dbyrne