java - 用于共享大型不可变对象(immutable对象)的工厂/缓存策略

标签 java scala

我的问题很像之前的帖子Optimal HashSet Initialization (Scala | Java) ,我想在其中使用 HashSet 进行加速(目前我正在使用 Set),但是 HashSet 没有展示其(恒定时间)优势。

对于提到的解决方案:

You can minimize the cost of equals by interning. This means that you acquire new objects of the class through a factory method, which checks whether the requested new object already exists, and if so, returns a reference to the existing object. If you assert that every object of this type is constructed in this way you know that there is only one instance of each distinct object and equals becomes equivalent to object identity, which is a cheap reference comparison (eq in Scala).

但是,我不太确定什么是有效的检查方式

whether the requested new object already exists

对于大对象(例如,带有 hashmap 参数的 case 类对象,一些其他对象结构...等)

通过比较每个复杂的字段并没有给出太多的性能优势,不是吗?或者如果是,还有其他方法吗?

另外,我也很疑惑如何制作

equals becomes equivalent to object identity, which is a cheap reference comparison (eq in Scala).

在代码中。

上面提到的intening技术,我觉得基本上就是一个对象缓存。 因此,我引用了帖子中提到的技术 Caching strategy for small immutable objects in Java? .但是,我仍然看不出大对象的有效方法是什么。

为了方便起见,我用///引用了帖子中的缓存技术(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 && p.x == x && p.y == y && p.z == z) /// Not working for large objects?
       return p;
    return POINTS[index] = new Point(x,y,z);
} 

总而言之,为大型对象实现高效缓存策略的最佳实践是什么,以便我可以利用 Scala 中的 HashSet

谢谢,

最佳答案

实习的目标是使 equals 方法能够使用引用相等来实现,如下所示: this eq that(或 Java 中的 this == that)。 很明显,此实现将具有最佳的运行时特性 比较一些字段集的传统 equals 越多。

这种比较只有在存在且只有一个实例时才有效 每个“唯一对象”由对象的某些字段集确定。

Intern-ing 只有在以下情况下才有效 实习生操作的前期成本 可以完全抵消调用(可能很多)equals 的最小成本, 由 HashMap 驱动。

正如您所注意到的,这个实习生-ing 可能需要一个潜在的昂贵的缓存机制: 有运行时开销(执行检查) 和内存开销(缓存的大小)。

  • 最直接的缓存方法是使用 HashMap 和传统的 equalshashCode 应该是惰性的;缓存它的结果,因此不需要重新计算。 可能需要考虑线程问题。

  • 实现这种缓存的一种方法是使用 trie ,可能在每个节点上用哈希表实现,并且每个“级别”对应于对象中的一个字段(第一级 - 字段 1,第二级,字段 2 等)用于“用于”的“字段集”建立独特性。”

还有其他可行的方法来实现这样的缓存。 请原谅我避免进一步讨论此类问题, 请允许我提出避免处理该问题的方法。

选项 1:无缓存

声明:您很可能通过以下方式获得足够有效的结果 使用快速哈希(并在内部缓存), equals 的“传统”实现, 并以 sufficient minimal sizeHashMapHashSet 开始

理想情况下哈希表中的冲突很少, 并且对 equals 的调用次数最少。

选项 2:将多个字段映射到一个唯一的 String

[此方法假定“唯一定义对象的字段”是不可变的。如果不是这样,可以进行适当的调整以进行补偿。]

构建并缓存一个对应于唯一对象实例的private unique: String。 例如,对于一些简单的对象,以下可能是唯一的:

The concatenation of the string values of the "set of fields used to establish uniqueness," separated by commas.

了解您的对象/场特征将有助于确定 如何创建这样一个独特的字符串。

有了这样的值,我们就可以避免单独的intern/caching机制 并通过实现 equalshashCode 保留大部分好处 就此 unique 字符串而言:

def equals(thatObj: Any) = thatObj match {
    case that : MyType => unique.equals(that.unique)
    case _             => false
  }

def hashCode() = unique.hashCode

选项 2 的替代方案:

[编辑:Rüdiger Klaehn 提供 this link which offers compelling evidence to avoid 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
  }

关于java - 用于共享大型不可变对象(immutable对象)的工厂/缓存策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18048669/

相关文章:

java - ARCore – 无法创建 AR session

scala - 在 Spark 中对可变集合建模

scala - 对重载定义的不明确引用,来自 Java 库

scala - 折叠过程中的中间值?

java - 如何从 bluJay 中的另一个类调用变量

java - Java 中的 lambda 表达式如何工作?

scala - 取 Struct 中 double 嵌套向量的平均值

java - 在 Maven 中指定源和测试文件夹的 IntelliJ 包前缀

java - Spring 配置问题 - 无法加载应用程序类

java - Spring-Maven : Unable to load Javascript file