您的位置:首页 > 编程语言 > Java开发

Java中equals方法和hashCode方法

2014-09-13 17:43 330 查看
《Effective Java》第2版的第9条条款要求“覆盖equals方法的类总要覆盖hashCode方法”,如果不这么做,就违反了Object.hashCode() 的通用约定,导致该类的实例无法与基于散列的集合(HashMap, HashSet, Hashtable)一起使用。

约定的内容包括:

1. 应用程序执行期间,只要对象equals方法的比较操作使用的信息没有被修改,前后调用hashCode的到的值应保持不变

2. 两个对象根据equals(Object)方法比较是相等的,则它们分别调用hashCode得到的值也应相等

3. 两个对象根据equals(Object)方法比较不相等,则它们的的hashCode返回的值不要求一定不相等

基于散列的集合通过hashCode方法计算得到的哈希码确定需要存放数据的散列桶,根据上述第3条描述,逻辑上不相等的对象有可能会产生相等的哈希码,所以哈希表有可能冲突,Java中的HashMap通过链地址法(链表)处理冲突。

如果某个类覆盖了equals方法,但没有覆盖hashCode方法,当该类的实例作为HashMap的key时,会出现问题:

import java.util.HashMap;
import java.util.Map;

public class PhoneNumber {
    private final int areaCode;
    private final int prefix;
    private final int lineNumber;
    
    public PhoneNumber(int areaCode, int prefix, int lineNumber){
        this.areaCode = areaCode;
        this.prefix = prefix;
        this.lineNumber = lineNumber;
    }
    @Override
    public boolean equals(Object o){
 if(this == o)
            return true;
        if(!(o instanceof PhoneNumber))
            return false;
        PhoneNumber pn = (PhoneNumber)o;
        return this.areaCode == pn.areaCode
                && this.prefix == pn.prefix
                && this.lineNumber == pn.lineNumber;
    }
    public static void main(String[] args){
        Map<PhoneNumber, String> map = new HashMap<PhoneNumber, String>();
        map.put(new PhoneNumber(707, 102, 888),  "Jenny");
        System.out.println(map.get(new PhoneNumber(707, 102, 888)));
    }
}
上述代码的执行结果是:null

为什么会出现这样的结果?因为PhoneNumber类并没有覆盖hashCode方法,所以会导致前后两个new PhoneNumber(707, 102, 888)的哈希码是完全没有关系的两个随机数,因此,后面从map中取数据时,也就不能获取存放之前数据的那个散列桶,所以无法获取希望的返回值“Jenny”;要解决这一问题,还需要重写hashCode方法。

那么怎样编写一个正确且有效的hashCode方法呢?正确性要求逻辑相等的对象,他们的hashCode必须返回相等的值;而有效性则要求不相等的对象尽可能获得不同的返回值。《Effective Java》中介绍了一种常用的方法:首先把某个整形常数值保存在result中;然后使用公式: result = result * 31 + c;c的计算方法如下:如果域的类型为byte, char, short, int,直接返回;如果为boolean,返回1或0;如果为float,返回Float.floatToIntBits(f);如果为long,返回前32位和后32位亦或操作后的返回值;如果为double,返回Double.doubleToIntBits(d)及long类型的操作后的值;对于引用类型,则递归调用下去。对于上述PhoneNumber的类型,其hashCode的定义如下:

@Override
public int hashCode(){
int result = 17;
result = result * 31 + areaCode;
result = result * 31 + prefix;
result = result * 31 + lineNumber;
return result;
}
添加上述代码后返回:Jenny

注:计算散列码时不要为了性能而忽略某个关键字段,虽然计算过程的效率提高了,但有可能会因此而增大散列冲突的概率,从而影响整体的效率
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: