Java中equals方法和hashCode方法

《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

注:计算散列码时不要为了性能而忽略某个关键字段,虽然计算过程的效率提高了,但有可能会因此而增大散列冲突的概率,从而影响整体的效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值