当前位置: 首页 > 知识库问答 >
问题:

用于共享大型不可变对象的工厂/缓存策略

鱼征
2023-03-14

我的问题与之前的后优化哈希集初始化(Scala | Java)非常相似,我想使用哈希集来加速(目前我正在使用哈希集),但哈希集没有显示其(恒定时间)优势。

对于提到的解决方案:

你可以通过实习来减少同等费用。这意味着您通过工厂方法获取类的新对象,工厂方法检查请求的新对象是否已经存在,如果已经存在,则返回对现有对象的引用。如果您断言这种类型的每个对象都是以这种方式构造的,那么您就知道每个不同对象只有一个实例,并且equals与object identity等价,这是一种廉价的引用比较(Scala中的eq)。

然而,我不太确定检查的有效方法是什么

请求的新对象是否已存在

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

通过比较这些复杂字段中的每一个并没有显示出多少性能优势,不是吗?或者如果是,还有其他方法吗?

此外,我也很困惑,如何

等于等于对象标识,这是一种廉价的引用比较(Scala中的eq)。

在代码中。

我认为上面提到的强化技术基本上是一个对象缓存。因此,我参考了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中的哈希集?

谢谢

共有1个答案

苏洛城
2023-03-14

interning的目标是使用引用等式实现等于方法,如:this eq that(或this==that,在Java中)。很明显,与比较一些字段集的更传统的equals相比,此实现将具有最佳的运行时特性。

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

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

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

>

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

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

声明:通过使用快速哈希(并在内部缓存),一个“传统”的equals实现,以及从足够小的哈希映射或哈希集开始,您可能会获得足够有效的结果

理想情况下,哈希表中几乎没有冲突,对equals的调用次数最少。

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

构建

“用于建立唯一性的字段集”字符串值的串联,用逗号分隔。

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

有了这样一个值,我们可以避免单独的实习生/缓存机制,并通过在这个唯一字符串中实现equalshashCode来保留大部分好处:

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

def hashCode() = unique.hashCode

[编辑:Rüdiger Klaehn提供了这个链接,提供了令人信服的证据来避免String.intern()]

使用字符串。实习并调整等于以利用它:

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
  }

 类似资料:
  • 我有一个Spring应用程序,它使用MyBatis进行持久化。我使用ehcache是因为速度对于这个应用程序很重要。我已经设置并配置了MyBatis和Ehcache。我使用一个名为“mybatis”的单一缓存,因为否则为每个实体创建单独的缓存将是荒谬的。 这是我的电子缓存。xml。 这是我的mybatis映射器界面的一个示例。 因为我有一个共享缓存,所以我需要一种方法使我的密钥对域对象是唯一的。作

  • 我对hibernate中如何使用第一级和第二级缓存有一些了解。与此相关的问题很多-是的,我同意 但我的困惑是。在第一级缓存中,除非我提交事务,否则我的数据不会持久化到db中,因此其他会话在此之前不会知道这些更改。如果二级缓存将实体带到会话工厂级别!这是否意味着即使在我提交事务之前,我在一个会话中的更改也可以被其他会话使用?? 使用二级缓存时何时更新到dB??在第一次,它将在结束事务时发生 我引用了

  • 我有两个应用程序使用相同的数据库实体。这两个应用程序都部署在jboss eap 6.2独立的集群上。DB表仅从一个应用程序中更新,但从两个应用程序中读取。这两个应用程序都使用本机hibernate API从数据库读取/写入数据。 在嵌入式模式下将infinispan启用为2LC后,如何确保在一个应用程序中更新的缓存实体从第二个应用程序缓存中失效?是否有任何JMX/JMS接口用于信号缓存失效? 若我

  • 问题内容: 我正在努力使可变对象与不可变对象有关。使用可变对象会带来很多负面影响(例如,从方法中返回字符串数组),但是我很难理解它的负面影响。使用可变对象的最佳实践是什么?您是否应尽可能避免使用它们? 问题答案: 好吧,这有几个方面。 没有参考身份的可变对象会在奇数时间导致错误。例如,考虑使用基于值的方法的 : 当实例用作键时,实例在映射中“丢失”,因为实例和相等性基于可变值。这些值在映射之外更改

  • 我正在使用WildFly 8.1,所以JPA 2.1和Hibernate 4.3.5 我想在WildFly中使用JPA共享缓存/二级缓存 我遵循WildFly文档:https://docs.jboss.org/author/display/WFLY8/JPA参考指南#使用InfinispanSecondlevelCache的JPA参考指南 这是我的persitience.xml: 我将属性设置为h

  • 我正在研究Joshua Bloch的Effective Java,他在书中解释了实现不可变类的不同方法。为了防止子类化,一种方法是使用 final。更复杂的方法是使构造函数私有,从而防止外部访问,并使用静态工厂创建对象。 但是,我不明白这个说法: 它最灵活,因为它允许使用多个包私有实现类。 我知道在没有公共/受保护的构造函数的情况下,外部客户端不可能对其进行子类化,但不了解术语“多包私有实现类”所