怎样书写一份像样的hashCode()

217 阅读2分钟

怎样书写一份像样的hashCode()


Java代码中时常需要用到HashMap,与HashMap脱不开关系的便是hashCode()方法,那么怎样书写一份像样的hashCode()成为一个问题。本文将结合《Java编程思想》一书和Java工具类源码,浅谈如何书写一份像样的hashCode()。

  1. 给int变量result赋予某个非零值常量,如17。

  2. 为对象内每个有意义的域f(即每个可以做equals()操作的域)计算出一个int散列码c;

    域类型计算
    booleanc = (f?0:1)
    byte、char、short或intc = (int)f
    floatc = Float.floatToIntBits(f)
    doublelong l =Double.doubleToLongBits(f) c = (int)(l ^ (l >>> 32))
    Object,其equals()调用这个域的equals()c = f.hashCode()
    数组对每个元素应用上述规则
  3. 合并计算的到的散列码

    result = 37 * result + c

  4. 返回result。

  5. 检查hashCode()最后生成的结果,确保相同的对象有相同的散列码。

public class CountedString {
    private static final List<String> created = new ArrayList<>(10);
    private final String s;
    private int id = 0;
​
    public CountedString(String string) {
        this.s = string;
        created.add(string);
        for (String s : created) {
            if (s.equals(string)) {
                id++;
            }
        }
    }
​
    @Override
    public String toString() {
        return "s='" + s + ''' +
                ", id=" + id +
                ", hashcode=" + hashCode();
    }
​
    @Override
    public boolean equals(Object o) {
        return o instanceof CountedString &&
                s.equals(((CountedString) o).s) &&
                id == ((CountedString) o).id;
    }
​
    @Override
    public int hashCode() {
        int result = 17;
        result = 37 * result + s.hashCode();
        result = 37 * result + id;
        return result;
    }
​
    public static void main(String[] args) {
        Map<CountedString, Integer> map = new HashMap<>();
        CountedString[] cs = new CountedString[5];
        for (int i = 0; i < cs.length; i++) {
            cs[i] = new CountedString("hi");
            map.put(cs[i], i);
        }
        System.out.println(map);
        for (CountedString c : cs) {
            System.out.println(c);
            System.out.println(map.get(c));
        }
    }
}

输出结果有兴趣的可以自己跑一下代码

以上均参考于《Java编程思想》第四版,陈昊鹏译

在欣赏一下Java自带的关于hashCode的工具方法

// Objects.hashCode(Object o) int
public static int hashCode(Object o) {
    return o != null ? o.hashCode() : 0;
}
​
// Objects.hash(Object... values) int
public static int hash(Object... values) {
    return Arrays.hashCode(values);
}
​
// Arrays.hashCode(long a[]) int
public static int hashCode(long a[]) {
    if (a == null)
        return 0;
    
    int result = 1;
    for (long element : a) {
        int elementHash = (int)(element ^ (element >>> 2));
        result = 31 * result + elementHash;
    }
    
    return result;
}
​
// Arrays.hashCode(int a[]) int
public static int hashCode(int a[]) {
    if (a == null)
        return 0;
​
    int result = 1;
    for (int element : a)
        result = 31 * result + element;
​
    return result;
}
​
// Arrays.hashCode(short a[]) int
...
int result = 1;
for (short element : a)
    result = 31 * result + element;
...
​
// Arrays.hashCode(char a[]) int
...
int result = 1;
for (char element : a)
    result = 31 * result + element;
...
​
// Arrays.hashCode(byte a[]) int
...
for (byte element : a)
...
    
// Arrays.hashCode(boolean a[]) int
...
for (boolean element : a)
    result = 31 * result + (element ? 1231 : 1237);
...
    
// Arrays.hashCode(float a[]) int
...
for (float element : a)
    result = 31 * result + Float.floatToIntBits(element);
...
​
// Arrays.hashCode(double a[]) int
...
for (double element : a) {
    double bits = Double.doubleToLongBits(element);
    result = 31 * result + (int)(bits ^ (bits >>> 32));
}
...
​
// Arrays.hashCode(Object a[]) int
...
for (Object element : a) {
    Object bits = Double.doubleToLongBits(element);
    result = 31 * result + (int)(bits ^ (bits >>> 32));
}
...
​
// Arrays.deepHashCode(Object a[]) int
// 该方法是关于多维数组的散列值方法,具体可查看响应源码