java-----hashCode和equals的区别

        首先来说说两者的区别,接着给出得出这些区别的原因,最后从HashMap和HashSet实现的角度谈谈这两个集合类对hashCode和equals的使用,其实说白了,个人认为这两个区别也只是在HashMap和HashSet中体现的比较明显点;

        两者的关系:

        (1):两个对象如果equals,那么他们的hashCode也相等
        (2):两个对象如果hashCode相等,但他们不一定equals
        (3):两个对象hashCode值不等,他们一定不equals
        (4):两个对象不equals,他们的hashCode值不一定不等

        也就是说我们在判断两个对象等不等的时候,首先判断两者的hashCode值等不等,不等的话两个对象直接就不等了,相等的话再去看equals,这点我们可以从HashMap的使用中体现出来;

        首先我们通过实例来具体看下这两者的区别:

        首先来印证结论(2):

public class Test {
	@Override
	public int hashCode() {
		System.out.println("执行了hashCode方法");
		return 1;
	}
	
	@Override
	public boolean equals(Object obj) {
		System.out.println("执行了equals方法");
		return false;
	}
	
	public static void main(String[] args) {
		HashMap<Test, String> map = new HashMap<>();
		Test test1 = new Test();
		Test test2 = new Test();
		map.put(test1, "test1");
		map.put(test2, "test2");
		System.out.println("map的长度:  "+map.size());
	}
}
        在此之前我们有必要看看HashMap的put方法实现原理了,查看源码如下:

 public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
        put方法的实现过程是首先调用hash(key)计算出当前key对应的hash值,在hash(key)方法中会执行当前key对象的hashCode方法,在计算出hash值之后,会调用indexFor算出当前hash值对应要存储到的数组的下标位置,因为HashMap是数组加链表实现的,数组部分就是通过我们key的hash值来对应位置的,而链表部分存储的是hash值相同的所有键值对,indexFor的目的就是找到当前hash值对应的数组下标,有了这个下标之后你会看到一个for循环,他会遍历当前数组下标里面的链表,通过调用key的equals方法来判断当前链表中是否有与将要插入的键值对的key值相同的元素,没有的话,会将当前当前键值对插入到这个链表的表头,如果有的话,则会替换掉原先已经存在的值;   

        可以看到,put方法首先调用的是hashCode,随后在找到对应hash值在数组中的存储位置之后才会执行equals方法找链表中有没有将要插入的键值对的key值的,为了方便,我们可以这样理解,hashCode找对应hash值在数组中的位置,equals找当前key值在该数组位置相应链表中位置的,直观点就是下面这幅图;

                     

         在测试中我们将Test类作为了HashMap的key值,随后调用了HashMap的put方法,接下来我们看看输出结果:

执行了hashCode方法
执行了hashCode方法
执行了equals方法
map的长度:  2
        第一行的输出是调用map.put(test1, "test1");执行的,第二行的输出是调用map.put(test2, "test2");执行的,因为上面测试中我们让Test类的hashCode方法的返回值都是1,那么此时所有put进map的key的hash值都是相同的,此时在 执行map.put(test2, "test2");的时候就需要调用equals方法来查看当前hash值对应的数组位置的链表中有没有与当前key equals的键值对存在,因为我们在Test类中令equals的返回值是false,所以肯定就不存在了,因此map的长度为2;

        我们修改上面的测试代码,将Test类中的equals方法的返回值修改为true,也就是修改成如下代码:

public class Test {
	@Override
	public int hashCode() {
		System.out.println("执行了hashCode方法");
		return 1;
	}
	
	@Override
	public boolean equals(Object obj) {
		System.out.println("执行了equals方法");
		return true;
	}
	
	public static void main(String[] args) {
		HashMap<Test, String> map = new HashMap<>();
		Test test1 = new Test();
		Test test2 = new Test();
		map.put(test1, "test1");
		map.put(test2, "test2");
		System.out.println("map的长度:  "+map.size());
	}
}
        查看输出:

执行了hashCode方法
执行了hashCode方法
执行了equals方法
map的长度:  1
        不同于上面的测试,这里的map大小变成了1,原因在于我们将equals返回值设置成了true,因为两次put操作hashCode方法返回值是相同的,所以他会执行equals查看当前hash值对应的数组位置处的链表中是否存在于当前key equals的键值对,因为equals始终返回true,那么就是存在了,根据上面对put源码的分析,当前最新的值将替换掉原先的值,不信你可以试试输出key值等于test1或者test2的值,结果都将是"test2";

        如果我们把测试代码修改成下面这样:

public class Test {
	public static int count = 0;
	@Override
	public int hashCode() {
		System.out.println("执行了hashCode方法");
		count++;
		return count;
	}
	
	@Override
	public boolean equals(Object obj) {
		System.out.println("执行了equals方法");
		return true;
	}
	
	public static void main(String[] args) {
		HashMap<Test, String> map = new HashMap<>();
		Test test1 = new Test();
		Test test2 = new Test();
		map.put(test1, "test1");
		map.put(test2, "test2");
		System.out.println("map的长度:  "+map.size());
	}
}
        查看输出:

执行了hashCode方法
执行了hashCode方法
map的长度:  2
        你会发现根本连equals方法都不会执行,原因在于我们让Test的hashCode方法每次都返回的值都是不一样的,这样两次put方法执行之后key值对应的hash值根本就不相等,也就是他们根本就是存储在数组中的不同下标处了,不会存储到同一下标处当然不用equals方法来查看到底是存储到下标对应处链表的哪个位置了;

        从上面的三个测试可以看出来,HashMap在put时候首先调用的是hashCode方法,如果发现当前hash值对应的数组位置处的链表为空的话是不会执行equals的,也就是hashCode方法先于equals方法执行;


       

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值