2016-11-14 100 views
1

假設我們有以下的情況下類:緩存斯卡拉案例類實例

abstract sealed class Tree 
case class Leaf(i: Int) extends Tree 
case class Node(left: Tree, right: Tree) extends Tree 

每次我們調用的情況下類的構造函數,一個新的對象在內存中創建。例如,在下面的代碼:

val a = Leaf(0) 
val b = Leaf(0) 

a和b點不同的對象存儲:

a == b // true 
a eq b // false 

我想覆蓋的情況下階層的「應用」的方法,使他們如果它已經存在,則返回一個緩存的對象,以便在上面的最小示例中,「a eq b」將返回true。

我在#1發現這兩個相關答案:

我計劃執行我的壓倒一切的「應用」方法與它結合了兩種方法ABO鏈接的方式緩存五個。但我想知道是否有其他方法可以考慮。如果你知道的話,可否請你在這裏分享你的解決方案? case類的

緩存實例似乎是做,以減少內存消耗一個非常有用的和自然的事情。然而,我打算實施的解決方案(基於上面鏈接的兩個答案)看起來相當複雜,需要大量的樣板代碼,這會損害案例類的優雅和簡潔。有誰知道,如果未來版本的Scala語言可能允許我們通過這樣寫簡單的東西來實現案例類實例緩存:

abstract sealed class Tree 
cached case class Leaf(i: Int) extends Tree 
cached case class Node(left: Tree, right: Tree) extends Tree 

??

+1

只是一個隨機問題,你看過這個「緩存」作爲任何其他語言的核心功能嗎?至少我還沒有遇到它。 –

+0

我也沒有。這讓我懷疑是否有這個原因。 @Alexey Romanov的回答下面指出了一些有趣的原因。 – Bruno

回答

2

緩存案例類的實例似乎是減少內存消耗非常有用和自然的事情。

注意,這甚至不是遠程自動改善,很大程度上取決於案件類的使用模式(不只是你的,但任何人誰使用您的圖書館):

  1. 你需要考慮內存緩存需求以及無法收集從緩存中引用的實例的垃圾回收(請注意,使用WeakHashMap將無濟於事:它要求「值對象不直接或間接強引用自己的密鑰」 )。

  2. 如果鍵是原語(如在Leaf中),則它們需要在查找之前被裝箱,這通常會是構造函數調用。

  3. 查找在地圖比一個平凡的構造函數調用顯著慢。

  4. 轉義分析通常會確保對象不是實際構建的,同時確保您的程序能夠像一樣工作。當然,緩存將確保對象轉義。

但是忽略了這一切,你可以寫一個宏的註釋,這將使你@cached case class Leaf(i: Int) extends Tree並生成你想要的代碼(或至少@cachedcase class;我不知道,如果你能夠覆蓋apply否則) 。由於上述原因,我不希望它很快成爲該語言的一部分。

+0

這些都是好的一般,即使其中一些不適用於我的特定使用模式。你能否詳細說明你的觀點4?我如何知道何時不會構建新對象?我可以在哪裏瞭解更多關於這方面的信 – Bruno

+0

您可以使用@cachedcase宏註釋提供一個最簡單的示例嗎? – Bruno

+1

有關基本解釋,請參閱http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html#escapeAnalysis。這個想法是,如果一個對象只在一個方法中使用,並不「逃脫」,它可以通過用局部變量替換所有字段來消除。但是檢查是否有任何特定的分配被消除是微不足道的(並且無論如何都需要JIT啓動,所以嘗試一次將不起作用)。見例如http://psy-lob-saw.blogspot.se/2014/12/the-escape-of-arraylistiterator.html。 –