在我的应用程序中,我需要能够交换集合中的元素。所以我有一个选择:
- 使用声明为的可变集合
val
- 或使用声明为的不可变集合
var
(并始终将新集合重新分配给var
)
但是在 Scala 中(在函数式编程中)总是避免可变性。那么更糟糕的是:使用声明的可变集合val
或声明为的不可变集合var
?
在我的应用程序中,我需要能够交换集合中的元素。所以我有一个选择:
val
var
(并始终将新集合重新分配给var
)但是在 Scala 中(在函数式编程中)总是避免可变性。那么更糟糕的是:使用声明的可变集合val
或声明为的不可变集合var
?
这实际上取决于您是否需要广泛共享该集合。可变集合的优点是它通常比不可变集合更快,并且更容易传递单个对象,而不必确保可以从不同的上下文中设置 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
将 a用于不可变集合通常更安全;那么你可能会过时,但至少你不会因为段错误而跌倒在你的脸上。
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!
所以,作为一般规则,
但是你应该尽可能经常地违反它,只要你坚持它。
如果您使用var
持有一个不可变集合,您可以相当自由地发布它(尽管您可能希望将其标记var
为@volatile
)。在任何给定时间,其他代码只能获得该状态的特定快照,并且永远不会改变。
如果您使用val
持有一个可变集合实例,那么您必须小心保护它,因为它可能会在更新时处于不一致的状态。
我使用的一种安全方法是这样工作的。首先隐藏你的 var 以使其线程安全,如下所示:
private[this] val myList: scala.collection.mutable.(whatever)
private[this] 不仅将变量限制在这个类中,而且仅限于这个确切的实例。没有其他变量可以访问它。
接下来,创建一个辅助函数以在外部需要使用它时返回它的不可变版本:
def myListSafe = myList.toList (or some other safe conversion function like toMap, etc)
执行这些转换可能会产生一些开销,但它使您可以灵活地在类中安全地使用可变集合 - 同时提供在需要时以线程安全的方式导出它的机会。正如您所指出的,另一种选择是不断改变指向不可变集合的 var 。
可变性是你最好关在笼子里的野兽。理想情况下,在经过良好测试和高度使用的笼子类型中,例如 scala mutable 集合。
同意 Rex,最大的担忧是您是否共享收藏。在这种情况下使用不可变的。另一种选择:
@volatile
private var myList = immutable.List.empty[Foo]
def theList = myList
对 a 的重新分配var
可能难以阅读/维护,尤其是如果这种情况发生在方法中的多个位置,例如。
因此,使用 a val
,您至少会获得一个不可变的引用。
一般来说,可变集合的范围应该尽可能小。因此,在您完全填充了可变集合之后(如果是短期缓冲区),您可以将其转换为不可变集合,例如将其作为方法的结果返回。
正如 dbyrne 正确指出的那样:如果不可能将可变集合转换为不可变集合(例如,如果您正在实现缓存),并且可变集合是公共 API 的一部分,那么 avar
可能会更好。在这种情况下,另一种选择是在实现公共 API 的方法中创建不可变副本。