0

我正在为 java 中的哈希映射构建一个复合键,并希望为这些对象中的每一个确定我自己的哈希码。我的问题是以下两种方法中最好的方法是什么。我的复合键具有三个 String 属性和一个 int 属性。

public int hashCode(){
    return (className + methodName + uniqueNumber).hashCode();
}

public int hashCode(){
    return (className + methodName + desc + uniqueNumber).hashCode();
}

我必须有 className、methodName 和唯一编号,以保证每个键都有唯一的哈希码。我想采用碰撞机会最小的方法。我的直觉是,我“添加”到哈希映射函数的属性越多,发生冲突的可能性就越小。但是,我并不完全确定这是正确的。

4

2 回答 2

2

您的问题有点不清楚,您需要/哪些字段足以唯一区分密钥。

通常,您应该通过乘以素数来组合单个散列(在复合键中)。

假设第一个例子:

public int hashCode() {
    int h = className.hashCode() * 23;
    h += methodName.hashCode() * 17;
    h += uniqueNumber;
    return h;
}

OTOH 如果uniqueNumber实际上是独一无二的,您可以简化:

public int hashCode() {return uniqueNumber;}

在您的评论中,您提到了一件事:“仅使用 uniqueNumber 将生成唯一的哈希值,但我将失去在哈希图中引用特定值的能力”。

现在这非常重要:“实例身份”是一个非常不同的哈希和查找的东西,与“价值”!您不能对两者使用相同的哈希码和映射。

例如,如果您需要一个 Key(ClassName, MethodName) -> SomeValue 查找,这将是一个“值”查找,并且需要通过 ClassName 和 MethodName 值进行散列,以便可以重复:即,您可以构造一个Map.get() 执行查找的键。

“Instance Identity”实际上已经内置了对 Java 中的散列和映射的支持——它被称为 IdentityHashMap。

但是对于大多数情况,包括可能用于映射的复合键,尤其是复合键,需要能够重新构造键以稍后执行查找。所以键应该有值语义,你uniqueNumber是否真的应该是键的一部分是值得怀疑的。

当您稍后进行查找时,如何获得正确uniqueNumber的检索数据?我的感觉是:

  1. 要么应该有一个一流的实体,您可以直接将其用作键(因此不再需要 CompositeKey 类),或者

  2. 你不能重复地得到uniqueNumber,在这种情况下它不起作用/无论如何都不需要。

总结一下:如果uniqueNumber真的需要或完全适用,我希望它已经被封装在一个一流的实体中。事实并非如此。看起来您很可能应该使用基于值的 key,并删除 uniqueNumber 位(至少从这里开始)。

所以我的建议:

public int hashCode() {
    int h = className.hashCode() * 23;
    h += methodName.hashCode() * 17;
    h += desc.hashCode();
    return h;
}

让我知道这是否有帮助。

于 2013-11-17T01:03:14.633 回答
0

一些评论;

(1) 哈希码不必是唯一的。事实上,它们通常不能保证是唯一的。在大多数情况下,保证唯一性的计算成本太高,也不可取。碰撞不是灾难性的。

(2) 哈希码应该反映对象实例的状态,而不是对象类。类名之类的东西不会进入它。当然,除非那是类的实例数据,例如在代表堆栈跟踪的一帧的类中,也许。

(3) 一个好的散列码会有大量的可能值,这些值会按概率分布,因此碰撞是不可能的。

(4) 在 Java 中,哈希码必须与Object.equals(). 请参阅 Javadoc 以java.lang.Object供参考。

于 2013-11-17T01:06:25.740 回答