制作哈希函数的最佳方法

时间:2013-11-17 00:40:31

标签: java performance hash hash-collision

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

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

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

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

2 个答案:

答案 0 :(得分: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将生成一个唯一的哈希值,但我将失去在hashmap中引用特定值的能力”。

现在这非常重要:“实例身份”是一个非常不同的东西哈希&查找,来自“价值”!你不能使用相同的哈希码&两者的地图。

例如,如果您需要一个Key(ClassName,MethodName) - > SomeValue查找将是一个“值”查找&需要通过ClassName&进行哈希处理MethodName值,以便可以重复:即,您可以为Map.get()构造一个键来执行查找。

“实例标识”实际上内置了对哈希和&amp ;;的支持。 Java中的地图 - 它叫做IdentityHashMap。

但对于大多数情况,包括&尤其是可能用于地图的复合键,需要能够重新构造密钥以便稍后执行查找。因此密钥应该具有值语义,并且您的uniqueNumber是否应该实际上是密钥的一部分是不确定的。

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

  1. 要么应该有一个第一类实体,你可以直接使用它作为键(所以不再需要CompositeKey类),或

  2. 您无法重复获取uniqueNumber,在这种情况下它无效/无论如何都不需要。

  3. 总结一下:如果uniqueNumber真的需要或适用,我希望它已经封装在一流的实体中。事实并非如此。看起来你应该最好使用基于值的键,并删除uniqueNumber位(至少从这里开始)。

    所以我的建议:

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

    如果这有帮助,请告诉我。

答案 1 :(得分:0)

一些评论;

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

(2)散列码应该反映对象实例的状态,而不是对象类。类名这样的东西不会进入它。当然,除非IS是类的实例数据,例如在表示堆栈跟踪的一帧的类中,否则可能。

(3)良好的哈希码将具有大量可能的值,并且这些值将以概率方式分布,以使冲突不可能。

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

相关问题