针对特定情况重写Java中的哈希代码

针对特定情况重写Java中的哈希代码,java,hashcode,overwrite,Java,Hashcode,Overwrite,我知道在超越hashCode和equals的同时,还有其他关于一般最佳实践的问题,但我有一个非常具体的问题 我有一个类,它有一个实例变量,一个相同类的数组。更明确地说,下面是代码: Class Node{ Node arr[] = new Node[5]; } 我需要覆盖类节点的hashCode,数组是决定两个节点是否相同的重要决定因素。如何有效地将数组合并到哈希代码的计算中 --编辑-- 我试图检查这两个节点是否相同,这意味着它们有相同数量的子节点,并且这些子节点导致完全相同的状态。

我知道在超越hashCode和equals的同时,还有其他关于一般最佳实践的问题,但我有一个非常具体的问题

我有一个类,它有一个实例变量,一个相同类的数组。更明确地说,下面是代码:

Class Node{
    Node arr[] = new Node[5];
}
我需要覆盖类节点的hashCode,数组是决定两个节点是否相同的重要决定因素。如何有效地将数组合并到哈希代码的计算中

--编辑--

我试图检查这两个节点是否相同,这意味着它们有相同数量的子节点,并且这些子节点导致完全相同的状态。因此,我正在有效地比较两个节点上的子树。我想知道我是否可以使用哈希来做这个相等性检查


我想我实际上需要散列整个子树,但考虑到我的类定义的递归性质,我不确定如何进行散列

包括http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html#hashCode(java.lang.Object[])作为hashCode()实现的一部分。

这取决于您的平等标准。数组中的顺序重要吗?如果是这样,您可能希望使哈希代码取决于数组中节点的顺序。如果没有,您可能需要对数组中所有节点的哈希代码进行异或运算。可能有些值是空的(所以要小心)

基本上,您需要一致地覆盖
hashCode
equals
,这样,如果两个对象相等,它们将具有相同的哈希代码。这是金科玉律

Eric Lippert有一个建议——这个建议同样适用于Java


需要注意的一个潜在问题是,如果节点中出现一个循环(节点B数组中出现对节点a的引用,反之亦然),则哈希代码计算中也可能出现一个循环。

可以使用
数组。hashCode()
数组。equals()
方法

我正在检查这两个节点 都是一样的,意思是 同样数量的孩子,而且 那些孩子导致了完全相同的结果 国家。因此,我是有效的 尝试在 两节点。我想知道我能不能用 散列以执行此相等性检查

不,不应使用哈希检查相等性。这不是它的目的。它最终可以帮助你找出物体是否相等,但它不会告诉你它们是否相等

相同的对象将生成相同的哈希值,但两个不相等的不同对象也可以生成相同的哈希值。换句话说,如果散列值不同,您肯定知道对象是不同的。就这样

如果要测试相等性,需要实现相等。在您的情况下,有一种危险,即您的方法将递归并引发堆栈溢出。如果对象包含对自身的引用,该怎么办

如果要生成哈希,可以考虑数组的大小(以及它是否为null),但我不会尝试使用数组中对象的哈希值,因为可能存在无限循环。它并不完美,但已经足够好了

还有另一种激进的方法也可以提供很好的结果。不要动态计算散列值,而是为每个节点对象实例设置一个随机int值(我的意思是在创建时一次性地返回该值)。在您的情况下,通过获取数组中对象实例的哈希值,您不会冒无限循环的风险

如果哈希值相等,则需要开始比较数组对象实例

REM:如果节点包含其他属性,则对这些其他属性计算哈希,并忽略数组。当且仅当两个对象之间的哈希相同时,才开始研究数组内容/大小

REM2:评论提到了DAG图,这意味着我们不会遇到递归性问题。但是,该条件不足以保证deepHashCode()将成功。此外,这也太过分了。有一种更有效的方法来解决这个问题

如果Node仅使用的哈希方法使用数组计算哈希值,则deepHashCode()可能有效。但这样做效率不高。如果哈希方法使用其他节点属性,那么这些属性也必须相等


有一种比较节点是否相等的更快方法。用唯一的编号标记每个节点实例。然后,要比较两个节点,首先比较它们的数组大小。如果等于,则使用每个数组中的节点的唯一编号比较它们。如果一个数组没有“拥有”另一个节点,那么我们就不能处理相等的节点。这个解决方案比递归要快得多。

如果性能有任何问题,可以在当前答案中添加几点

首先,您需要决定子节点在节点中的顺序是否重要。如果没有,则不能对数组使用哈希代码。考虑在代码中使用java代码。还考虑在内部使用一些排序来改善相等的性能。例如,如果子树的深度/高度不同,则可以按深度排序

第二,如果子树很深,哈希代码可能会非常昂贵。因此,我会缓存hashcode,并在构建时进行计算(如果您的节点是不可变的),或者在发生变异时使其失效,然后根据需要重新计算

第三,如果子树很深,请检查equals()中的hashcode并尽早返回false。是的,hashcode由映射实现检查,但在某些地方,代码只是使用equals()比较两个对象,它们可能会付出很大的代价

最后,考虑使用数组.asList.()(如果是子排序问题)或HashSet(如果排序)