2016-11-14 98 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。 –