Effective Java 学习笔记(9)

来源:互联网 发布:柴田胜家 知乎 编辑:程序博客网 时间:2024/05/16 18:59

通常,有一类问题的产生都是由于没有重载hashCode这个方法. 当你重载equals后,hashCode必须重载,否则在使用HashMap, HashSet, Hashtable的时候,会出错.

对于hashCode有几个原则.

1. 不管什么时候调用hashCode,如果同一个对象与hashCode相关的字段没有修改的话,这个方法的返回值必须相同. 但不必和另一个应用程序中的值相等.

2. 当两个对象equals的时候,其hashCode也必须相同.

3.没有强制两个不一样的对象不能返回相同的hashCode, 但是要注意的,如果Hash表中冲突太多,会降低程序性能.

 

一个好的hashCode函数,能对不同的对象返回不同的值。写一个好的hashCode不是想像的那么困难,如果参照以下的方法的话:

 

1. 储存一个非零常量,如17,作为返回值。

 

2. 对每一个有比较意义的成员,参照以下:

 a. 计算数字型的hash code

     i.  如果是boolean, 计算(f?1:0)

     ii. 如果是byte char,short or int 计算,(int)f

     iii. 如果是long, 计算(int) ( f ^ (f >>>32))

     iv. 如果是float, 计算Float.floatToIntBits(f);

     v. 如果是double, 计算Double.doubleToLongBits(f), 得到长整型数字请参见 iii.

     vi. 如果是个对象的引用,就调用这个对象的hashcode

     vii,如果是数组,就对每个元素按上述原则做。

 b.  计算hashCode 按以下公式。

  result = 31 * result + c;   // c是 上面计算的值,选择31有两个原因,一是它足够大的素数,二是它能够被虚拟机优化 i * 31 = ( i << 5 -1)

 

3. 返回值。

4. 写测试用例测试。

 

要注意的是,你可以在hashCode中忽略一些值,但是同样,这些值也不能在equals中引用。

 

@Override public int hashCode() {
int result = 17;
result = 31 * result + areaCode;
result = 31 * result + prefix;
result = 31 * result + lineNumber;
return result;
}

 

如果一个对象是不可变的,则可采用另一种方式。

 

private volatile int hashCode;

 

@Override public int hashCode() {


        int result = hashCode;
        if (result == 0) {
                result = 17;
                result = 31 * result + areaCode;
                result = 31 * result + prefix;
                result = 31 * result + lineNumber;
                hashCode = result;
        }
        return result;
}

 

注意不要在函数中少引用一些重要字段这种方式来提高hashCode的性能,这会导致hashTable中冲突加剧带来更大的性能问题。

 

 

原创粉丝点击