用于共享大型不可变对象的工厂/缓存策略
我的问题很像以前的postOptimal HashSet Initialization(Scala | Java) ,我想使用HashSet
加速(目前我正在使用Set
),但HashSet
没有表现出它的(恒定时间)优势。
对于提到的解决方案:
您可以通过实习来最小化等于的成本。 这意味着您通过工厂方法获取类的新对象,该方法检查所请求的新对象是否已存在,如果是,则返回对现有对象的引用。 如果断言此类型的每个对象都是以这种方式构造的,那么您就知道每个不同对象只有一个实例,而equals等同于对象标识,这是一个廉价的引用比较(Scala中的eq)。
但是,我不太清楚检查的有效方法是什么
请求的新对象是否已存在
对于大对象(例如,带有hashmap参数的case类的对象,一些其他对象结构……等)
通过比较每个复杂的领域不会给出太多的性能优势,不是吗? 或者如果是,还有其他方法吗?
另外,我也很困惑如何制作
equals等同于对象标识,这是一个廉价的参考比较(Scala中的eq)。
在代码中。
我认为,上面提到的intening技术基本上是一个对象缓存。 因此,我参考了Java中小型不可变对象的后缓存策略中提到的技术? 。 但是,我仍然没有看到大型物体的有效方式。
为方便起见,我引用了post中的缓存技术(用Java), ///
表示我的想法和问题:
private static final int N_POINTS = 10191; private static final Point[] POINTS = new Point[N_POINTS]; public static Point of(int x, int y, int z) { int h = hash(x,y,z); /// I can use hash code of each complicated field to construct the value int index = (h & 0x7fffffff) % N_POINTS; Point p = POINTS[index]; if (p != null && px == x && py == y && pz == z) /// Not working for large objects? return p; return POINTS[index] = new Point(x,y,z); }
总而言之,为大型对象实现高效缓存策略的最佳实践是什么,以便我可以利用Scala中的HashSet
?
谢谢,
实习的目标是使用引用相等来实现equals
方法: this eq that
(或者this == that
Java中的this == that
)。 很明显,与比较某些字段集的更传统的 equals
相比,此实现具有最佳的运行时特性。
只有当对象的某些字段集合确定每个“唯一对象”中只有一个实例时,此比较才有效。
只有在实际操作的前期成本可以被HashMap
驱动的(可能很多)调用equals
的最小化成本完全抵消时, 实习才有效。
正如您所指出的,此实习可能需要一个可能代价高昂的缓存机制:运行时开销(执行检查)和内存开销(缓存大小)。
-
缓存最直接的方法是使用
HashMap
,传统的equals
。hashCode
应该是懒惰的; 缓存它的结果,因此不需要重新计算。 可能需要考虑线程问题。 -
实现这样的高速缓存的一种方法是使用trie ,可能在每个节点处使用散列表实现,并且其中每个“级别”对应于对象中的字段(第一级 – 字段1,第二级,字段2等。 …)用于“用于建立唯一性的字段集”。
还有其他可行的方法来实现这样的缓存。 请原谅我避免进一步讨论此类问题,并允许我提出避免处理问题的方法。
选项1:没有缓存
声明: 通过使用快速哈希(并在内部缓存), equals
的“传统”实现,以及具有足够最小大小的HashMap
或HashSet
,您可能会获得足够有效的结果
理想情况下,哈希表中的冲突很少,并且对equals
的调用次数最少。
选项2:将多个字段映射到一个唯一的String
[此方法假定“唯一定义对象的字段”是不可变的。 如果不是这样,可以进行适当的调整以补偿。]
构建和缓存private unique: String
与唯一对象实例对应的private unique: String
。 例如,对于某些简单对象,以下内容可能是唯一的:
用逗号分隔的“用于建立唯一性的字段集”的字符串值的串联。
了解对象/字段特征将有助于确定如何创建这样一个唯一字符串。
有了这样的值,我们就可以避免单独的实习/缓存机制,并通过在这个unique
字符串方面实现equals
和hashCode
来保留很多好处:
def equals(thatObj: Any) = thatObj match { case that : MyType => unique.equals(that.unique) case _ => false } def hashCode() = unique.hashCode
选项2的替代方案:
[编辑:RüdigerKlaehn提供了这个链接,提供了令人信服的证据,以避免String.intern() ]
使用String.intern
并调整equals
以利用它:
private val unique = buildUnique().intern def equals(thatObj: Any) = thatObj match { case that : MyType => unique.eq(that.unique) // In Java: unique == that.unique case _ => false }