Java:获取对象的唯一哈希值

时间:2011-02-15 05:09:29

标签: java

我正在尝试为Java对象获取唯一的哈希值,例如以下内容:

  1. If A == B then A.HashValue() == B.Hash.HashValue()
  2. If A != B then A.HashValue() != B.HashValue()
  3. 假设该对象包含几个布尔和整数字段。

6 个答案:

答案 0 :(得分:7)

//非常重要的编辑......

Gjorgji,我知道您接受以下答案是正确的,但我发现它是不正确的。

如果您有这样的课程:

class tiny {
    int a;
    public int hashCode() { return a; }
}

您已经超出了所有可能的哈希码。 (如果不清楚原因,请说出来。)

因此,如果您向对象添加任何其他信息,如果您希望在hashCode中表示该信息,那么您将在某处发生冲突。

但是,就此而言,你真的不想开始获得一个对象100%唯一的hashCode。那真的不是hashCode的意思!

hashCode的目的是为对象提供一个“足够唯一”的标识符,以便将其放在哈希桶中。它不是用于识别,而是用于分类。这个想法是,如果你有一大堆对象,你可能不会有很多碰撞,所以如果按照hashCode对项目进行分组,你可能会很快访问你正在寻找的东西。

如果这意味着您取消选择我的答案是正确的,那没关系。对于你正在寻找的东西,这是不正确的。我的希望是你意识到hashCode的这个解释会引导你正确使用,从而保持正确性。但正如马克明确指出的那样,这实际上并没有解决你所说的问题。

以下是旧答案:

=============================================== ============

这里有一篇关于它的好文章,来自Effective Java(最好的“我想学习如何成为一名优秀的Java开发人员”一书)。

http://www.linuxtopia.org/online_books/programming_books/thinking_in_java/TIJ313_029.htm

class Gjorgji {
    boolean a;
    boolean b;
    boolean c;
    int x;
    int y;

    // EDIT: I almost forgot a VERY important rule...
    // WHEN YOU OVERRIDE hashCode, OVERRIDE EQUALS (and vice versa)
    public int equals(Object o) {
        if(!(o instanceof Gjorgji) return false;
        Gjorgji g = (Gjorgji)o;
        return a == g.a && b == g.b && c == g.c && x == g.x && y == g.y;

    }

    public int hashCode() {
        int hash = x ^ y;
        hash *= a ? 31 : 17; // pick some small primes
        hash *= b ? 13 : 19;
        hash *= c ? 11 : 29;
        return hash;
    }

}

答案 1 :(得分:4)

这通常是不可能的,您必须保证a.equals(b)a.hashCode() == b.hashCode()。你不能保证反过来:你总是可以有冲突,因为hashCode方法只有32位空间,你的JVM可以有64位空间用于身份哈希码。

答案 2 :(得分:3)

如果您可以将班级的实例数限制在2 ^ 32以下,则可以执行此操作。这是一种方式:

import java.util.concurrent.atomic.AtomicInteger;

class UniqueHash {
    private static AtomicInteger NEXT_HASH_CODE = new AtomicInteger();
    private final int hashCode;

    UniqueHash() {
        while (true) {
            int nextHashCode = NEXT_HASH_CODE.get();
            if (nextHashCode == -1) {
                throw new RuntimeException("Too many instances!");
            }
            if (NEXT_HASH_CODE.compareAndSet(nextHashCode, nextHashCode + 1)) {
                hashCode = nextHashCode;
                break;
            }
        }
    }

    public int hashCode() {
        return hashCode;
    }
}

编辑:这假设“a == b”在对象身份意义上意味着a == b。您在评论中提到,如果字段相等,您实际上是指。请参阅@Mark Peters和@sjr的回复。

编辑2:由@Tom Hawtin指出的修复错误 - 强调,留下了其他不良做法。 :)

编辑3:我的“修复”中有一场比赛。修正了比赛。

答案 3 :(得分:3)

  

我正在尝试为Java对象获取一个唯一的哈希值...让我们说该对象包含几个布尔和整数字段。

要执行此操作,您需要的时间长于32位整数,或者您需要在字段范围内定义约束。将32位以上的信息填充到32位根本不可能,并且只有int和boolean就是33位信息(假设int的每个值都是可能的)。

如果你有多个int字段,long甚至不够大。您需要进入BigIntegerBitSet或字节数组。

无论如何,说你的数据不超过32位。然后,只需将数据安排到由int表示的位字段中。

byte a;
byte b;
boolean c;
boolean d;

int hash = (a << 24) | (b << 16) | (c ? 0x02 : 0) | (d ? 0x01 : 0);


//layout
//index:                         ... 3210                             
//aaaa aaaa bbbb bbbb 0000 0000 0000 00cd

这不会使其成为分布均匀的哈希(例如,在哈希表中使用)。但是,如果你想保证唯一性,你可能不会尝试将它用于哈希表吗?

我很好奇为什么你有这个奇怪的要求。散列的正常目的是获得可能唯一但具有固定(缩小)大小的值。您的要求保证哈希需要与它所代表的数据一样宽。

答案 4 :(得分:1)

使用System.identityHashCode()

http://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#identityHashCode(java.lang.Object)

编辑:使用此方法无法保证哈希码的唯一性;但是,考虑到你无法获得Object的内存位置,我认为这是你能做的最好的事情。你提出的任何其他散列函数都必须具有两个结构等效的对象散列到相同值的属性,而这个函数至少为你提供了程序创建的具有不同散列码的所有对象的机会。

为了完整性:Object的默认哈希码在构造Object时从其初始内存位置计算一次。因此,如果使用相同的初始内存位置创建多个Object,则它们必须具有相同的哈希码。

答案 5 :(得分:1)

如何获得“唯一ID” - 我不建议这样做 :-)但是,它确实满足了问题的要求。请参阅IdentityHashMap并考虑弱引用。

  1. 使用地图对象 - &gt;整数,其中整数表示计数器。
  2. 对于看到的每个新对象,递增计数器并将其添加到Map。
  3. 对于每个现有对象,返回存储的值。
  4. 也可能存在特定于实现的方法:例如,在Sun上,我相信Object.toString(基本方法)总是返回每个对象生存期的唯一字符串。 “编码号码”可以被抽出并且是“内部参考”AFAIK。

    我不保证前一段的准确性。因人而异。快乐的编码。