Java为两个可互换的整数重写equals()和hashcode()

时间:2013-04-08 11:30:30

标签: java equals hashcode hash-code-uniqueness

我正在为两个整数的简单容器对象重写equals和hashcode方法。每个int都反映了另一个对象的索引(该对象是什么并不重要)。该类的要点是表示两个对象之间的连接。

连接的方向无关紧要,因此无论两个整数在对象Eg中的哪个方向,等于方法都应该返回true。

connectionA = new Connection(1,2);
connectionB = new Connection(1,3);
connectionC = new Connection(2,1);

connectionA.equals(connectionB); // returns false
connectionA.equals(connectionC); // returns true

这是我所拥有的(从Integer的源代码修改):

public class Connection {
    // Simple container for two numbers which are connected.
    // Two Connection objects are equal regardless of the order of from and to.

    int from;
    int to;

    public Connection(int from, int to) {
        this.from = from;
        this.to = to;
    }

    // Modifed from Integer source code
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Connection) {
            Connection connectionObj = (Connection) obj;
            return ((from == connectionObj.from && to == connectionObj.to) || (from == connectionObj.to && to == connectionObj.from));
        }
        return false;
    }

    @Override
    public int hashCode() {
        return from*to;
    }
}

这确实有效但我的问题是:有没有更好的方法来实现这个目标?

我主要担心的是hashcode()方法将为任意两个整数返回相同的哈希码,这两个整数相乘相同的数字。 E.g。

3*4 = 12
2*6 = 12 // same!

文档http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#hashCode()声明

  

如果两个对象根据不相等而不是必需的   equals(java.lang.Object)方法,然后调用hashCode方法   两个对象中的每一个都必须产生不同的整数结果。   但是,程序员应该意识到产生了不同的   不等对象的整数结果可以提高性能   哈希表。

如果有人能看到一种减少匹配哈希码数量的简单方法,那么我会很感激答案。

谢谢!

PS我知道有一个java.sql.Connection可能会导致一些导入烦恼。该对象实际上在我的应用程序中有一个更具体的名称,但为了简洁起见,我将其缩短为Connection here。

5 个答案:

答案 0 :(得分:6)

已提出三种“有效”的解决方案。 (通过工作,我的意思是它们满足哈希码的基本要求......不同的输入提供不同的输出......并且它们还满足OP的额外“对称性”要求。)

这些是:

   # 1
   return from ^ to;

   # 2
   return to*to+from*from;

   # 3
   int res = 17;
   res = res * 31 + Math.min(from, to);
   res = res * 31 + Math.max(from, to);
   return res;

第一个问题是输出范围受实际输入值范围的限制。因此,例如,如果我们假设输入分别是小于或等于2 i 和2 j 的非负数,那么输出将小于或等于2 MAX(I,J)。这可能会给你的哈希表中的“分散” 1 带来较差......并且碰撞率更高。 (from == to时也有问题!)

第二个和第三个比第一个更好,但如果fromto很小,你仍然可能会遇到更多的碰撞。


如果最小化fromto的小值的冲突,我建议使用第4种替代方法。

  #4
  int res = Math.max(from, to);
  res = (res << 16) | (res >>> 16);  // exchange top and bottom 16 bits.
  res = res ^ Math.min(from, to);
  return res;

这样做的好处是,如果fromto都在0..2 16 -1范围内,则每个不同的(无序)都会得到一个唯一的哈希码)对。


1 - 我不知道这是否是正确的技术术语...

答案 1 :(得分:3)

这是一种被广泛接受的方法:

@Override
public int hashCode() {
    int res = 17;
    res = res * 31 + Math.min(from, to);
    res = res * 31 + Math.max(from, to);
    return res;
}

答案 2 :(得分:2)

我认为,像

@Override
public int hashCode() {
    return to*to+from*from;
}

足够好

答案 3 :(得分:1)

通常我将XOR用于哈希码方法。

@Override
public int hashCode() {
    return from ^ to;
}

答案 4 :(得分:0)

我想知道为什么没有人提供通常最好的解决方案:规范化您的数据

 Connection(int from, int to) {
      this.from = Math.min(from, to);
      this.to = Math.max(from, to);
 }

如果不可能,那么我建议像

 27644437 * (from+to) + Math.min(from, to)
  • 通过使用不同于31的乘数,可以避免this question中的碰撞。
  • 通过使用较大的乘数,可以更好地传播数字。
  • 使用奇数乘数确保乘法是双射的(即没有信息丢失)。

  • 通过使用 prime ,你可以获得什么,但是每个人都这样做,并且它没有任何劣势。