2012-12-04 67 views
18

如果我在不可變的Set集合上調用toSeq,我會得到一個ArrayBuffer爲什麼Scala的toSeq將一個不可變的Set轉換爲一個可變的ArrayBuffer?

scala> Set(1,2,3).toSeq // returns Seq[Int] = ArrayBuffer(1, 2, 3) 

這令我感到奇怪。鑑於Scala強調使用不可變數據結構,我期望獲得不變的序列,如VectorList而不是可變的ArrayBuffer。返回的集合元素的順序當然應該是未定義的,但似乎沒有任何語義上的原因,爲什麼該順序也應該是可變的。

在一般情況下,我希望斯卡拉操作總是產生不可變的結果,除非我明確要求一個可變的一個。這一直是我的假設,但這是一個不正確的,我實際上花了一個小時調試一個問題,其中ArrayBuffer的意外存在導致match語句中的運行時錯誤。我的修補程序是將Set(...).toSeq更改爲Set(...).toList,但這種感覺像是黑客,因爲我的應用程序沒有任何特別需要列表的地方。

是具有Set(...).toSeq返回可變對象的一個​​漏洞在Scala的實現,或者是有我在這裏誤解原則?

這是斯卡拉2.9.2。

+0

固定在2.10-RC2:'階> SEQ(1,2,3).toSeq' - >'RES0:序號[INT] =列表(1,2,3) ' – senia

+0

@senia,你的意思是設置(1,2,3).toSeq而不是'Seq(1,2,3).toSeq'? –

+1

請投票修改:[SI-6570](https://issues.scala-lang.org/browse/SI-6570) –

回答

11

Here是序列是否意味着immutable.Seq近期線程。

羅蘭·庫恩:

collection.Seq沒有改變者是不是在所有有效的防禦!

可變參數值的例子是相當鬼鬼祟祟的。

最近,

scala> Set(1,2,3) 
res0: scala.collection.immutable.Set[Int] = Set(1, 2, 3) 

scala> res0.toSeq 
res1: Seq[Int] = ArrayBuffer(1, 2, 3) 

scala> res0.to[collection.immutable.Seq] 
res2: scala.collection.immutable.Seq[Int] = Vector(1, 2, 3) 
+0

看起來目前的答案是「這是有爭議的」,並且鏈接的線程給出了一個好的討論爲什麼。查看關於原始問題的評論中的鏈接,因爲從他們看來,反變性主義者似乎正在得到他們的方式。 –

10

我同意這有點奇怪,但我不認爲這是一個缺陷。首先,考慮這樣的:Set.toSeq編譯時類型

() => Seq[Int] 

() => ArrayBuffer[Int] 

ArrayBuffer恰好是返回對象的運行時類型(可能是因爲Set.toSeq增加的ArrayBuffer然後只是返回而不轉換)。

因此,即使toSeq是給你回一個可變對象,你不能真正發生變異它(不含鑄造,或模式匹配ArrayBuffer - 所以真正的「奇怪」的是,斯卡拉讓您在模式匹配任意類)。 (你必須相信Set不會堅持這個對象並進行變異,但我認爲這是一個公平的假設)。

另一種方式來看待它是一個可變的類型只是嚴格小於一個不變的類型更具體。或者,另一種說法是,每個可變對象也可以被當作一個不可變對象:一個不可變對象有一個getter,一個可變對象有一個getter setter - 但它仍然有一個getter。

當然,這可能會被濫用:

val x = new Seq[Int] { 
    var n: Int = 0 
    def apply(k: Int) = k 
    def iterator = { 
     n += 1 
     (0 to n).iterator 
    } 
    def length = n 
} 

x foreach println _ 
0 
1 

x foreach println _ 
0 
1 
2 

但是,好,所以會有很多的東西。

相關問題