在我的應用程序中,我需要在集合中交換元素的能力。所以我有一個選擇:可變變量與可變集合
- 使用可變集合聲明爲
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