hashCode和equals

    hashCode和equals用来标识对象,两个方法协同工作可以用来判断两个对象是否相等。众所周知,根据生成的哈希将数据离散开来,可以使存取元素更快。对象通过调用Object.hashCode()生成哈希值;由于不可避免地会存在哈希值冲突的情况,因此当hashCode相同时,还需要再调用equals进行一次值的比较;但是,若hashCode不同,将直接判断Objects不同,跳过equals,这加快了冲突处理效率。Object类定义中对hashCode和equals要求如下:

  1. 如果两个对象的equals的结果是相等的,则两个对象的hashCode的返回结果也必须是相同的。
  2. 任何时候覆写equals,都必须同时覆写hashCode。

    在Map和Set类集合中,用到这两个方法时,首先判断hashCode的值,如果hash相等,则再判断equals的结果,HashMap的get判断代码如下:

if(e.hash == hash&&((k = e.key) == key || (key != null && key.equals(k)))
   return (e = getNode(hash(key), key) == null ? null : e.value;

    if条件表达式中的e.hash==hash是先决条件,只有相等才会执行后面的判断。如果不相等,则后面的equals根本不会被执行。equals不相等时并不强制要求hashCode也不相等,但是一个优秀的哈希算法应尽可能地让元素均匀分布,降低冲突概率,即在equals不相等时尽量使hashCode也不相等,这样&&或||短路操作一旦生效,会极大地提高程序的执行效率。如果自定义对象作为Map的键,那么必须覆写hashCode和equals。此外,因为Set存储的不是重复的对象,依据hashCode和equals进行判断,所以Set存储的自定义对象也必须覆写这两个方法。此时如果覆写了equals,而没有覆写hashCode,具体会有什么影响,让我们通过如下代码深入体会:

public class EqualsObject{
	private int id;
	private String name;
	public EqualsObject(int id, String name){
		this.id = id;
		this.name = name;
	}

	@Override
	public boolean equals(Object obj){
		//如果为null,或者并非同类,则直接返回false(第一处)
		if(obj == null || this.getClass() != obj.getClass()){
			return false;
		}

		//如果引用指向同一个对象,则返回true
		if(this == obj){
			return true;
		}
		//需要强制转换来获取EqualsObject的方法
		EqualsObject temp = (EqualsObject)obj;
		//本示例判断标准是两个属性值相等,逻辑随业务场景不同而不同
		if(temp.getId() == this.id && name.equals(temp.getName())){
			return true;
		}
		return false;
	}

	//getter and setter...
}

    第1处说明:首先判断两个对象的类型是否相同,如果不匹配,则直接返回false。此处使用getClass的方式,就是严格限制了只有EqualsObject对象本身才可以执行equals操作。
    这里并没有覆写hashCode,那么把这个对象放置到Set集合中去:

Set<EqualsObject> hashSet = new HashSet<>();
EqualsObject a = new EqualsObject(1,"one"));
EqualsObject b = new EqualsObject(1,"one"));
EqualsObject c = new EqualsObject(1,"one"));

hashSet.add(a);
hashSet.add(b);
hashSet.add(c);
System.out.println(hashSet.size());

    输出的结果是3.虽然这些对象显而易见是相同的,但在HashSet操作中,应该只剩下以一个,为什么结果是3呢?因为如果不覆写hashCode(),即使equals()相等也毫无意义,Object.hashCode()的实现是默认为每一个对象生成不同的int的数值,它本身是native方法,一般与对象内存地址有关。
    因为EqualsObject没有覆写hashCode,所以得到的是一个与对象地址相关的唯一值,回到刚才的HashSet集合上,如果想存储不重复的元素,那么需要在EqualsObejct类中覆写hashCode():

@Override
public int hashCode(){
	return id + name.hashCode();
}

    EqualsObject的name属性是String类型,String覆写了hashCode(),所以可以直接调用.equals()的实现方式与类的具体处理逻辑有关,但又各不相同,因而尽量分析源码来确定其判定结果。1


  1. 码出高效 Java开发手册 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值