==和equals的区别,为什么要同时重写equals和hashCode?

1 基础数据类型

  • 基础数据类型只有==,
  • 对于基本数据类型,==比较的是值,没有equals()方法。对于引用数据类型来说,==比较的是对象的内存地址,equals()用来判断两个对象是否相等

2 引用数据类型

  • ==比较的是引用类型对象在内存中存放的地址。准确的说是堆内存中的地址
  • equals() 是基类Object自带的方法,我们写的每个类都会有这个默认的方法,你可以重写它,也可以不重写
    在这里插入图片描述

2.1 不重写equals()

  • 当我们自己写的类没有重写equals方法时,equals等同于==,比较的是引用类型对象在内存中存放的地址
class A{
	int a=0;
	public A(int a){
		this.a=a;
	}
}
 
public class Test {
	public static void main(String[] args) {
		A a1=new A(1);
		A a2=new A(1);
		// 当没有重写toString方法的时候可以这么打印它们的地址
		System.out.println(a1);		// Chapter_one_02.A@6d06d69c
		System.out.println(a2);		// Chapter_one_02.A@7852e922
		// 没有重写equals方法,所以equals等同于==,比较的是引用类型对象在内存中存放的地址
		System.out.println(a1==a2);			// fasle
		System.out.println(a1.equals(a2));	// false
	}	

2.2 重写equals()

  • 重写了equals方法,所以在调用equals方法的时候,就不等同于==,进行值的比较
String s1=new String("a");
String s2=new String("a");
System.out.println(s1==s2);				// false
System.out.println(s1.equals(s2));		// true

在这里为什么equals是true呢?创建两个对象,地址肯定不一样。这是因为String重写了equals()方法,我们来看下String类的equals方法:
在这里插入图片描述

3、如何查找要被重写的方法

在eclipse中,鼠标移动到方法名上,ctrl+ t,就可以看到源码。如果发生Source not found ,参考eclipse中Source not found解决方法

4、重写equals()最好重写hashCode()

4.1 equals()

4.1.1 没有重写的equals()
public boolean equals(Object obj) {
        return (this == obj);
}

重写前的equals()方法通过其引用地址值来进行比较

4.1.2 重写的equals()

常用的String类,重写的equals()方法如下:

// 重写equals方法
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

这里的比较已不再是单纯的地址比较了

  • 首先通过地址进行比较,如果地址相同那么肯定是相同的对象
  • 如果地址不同就再拿char数组的内容进行比较,完全相等则返回true

也就是说虽然是两个String对象,但是它们的字符串值相等,那么equals方法返回的结果就是true。这也正是大多数情况下我们所说的 “equals方法比较的是值” 。

4.2 hashCode()源码

4.2.1 没有重写的hashCode()
public native int hashCode();

它是一个native方法,可以根据平台自行实现,但它对其实现提出了一定的要求:

  • hashCode()是一个native方法,返回值类型是整形,并且可以被重写
  • Object中的native hashCode()方法将对象在内存中的地址作为哈希码返回,可以保证不同对象返回的哈希值不同();但是,在同样的条件下,同一个对象无论运行多少次,返回的哈希值必须一样
  • 如果两个对象通过equals()方法比较,判定相等,那么,调用二者各自的hashCode()方法必须产生同一个hash值。如果两个对象通过equals()方法比较,判定不相等,并不要求调用二者各自的hashCode()方法必须产生不同的hash值。
4.2.2 重写的hashCode()

还以String类为例,重写的hashCode()方法为:

// 重写hashCode方法
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

上述hash值的计算公式:

  • 基本公式为:s[0]*31(n-1) + s[1]*31(n-2) + … + s[n-1]。其中, s[i]是字符串的第i个字符,n是字符串的长度,^表示求幂(空字符串的哈希码为0)

计算过程中使用数字31,主要有以下原因:

  • 由于质数的特性,它与其他数字相乘之后,计算结果唯一的概率更大,哈希冲突的概率更小。
  • 使用的质数越大,哈希冲突的概率越小,但是计算的速度也越慢;31是哈希冲突和性能的折中,实际上是实验观测的结果。
  • JVM会自动对31进行优化:31 * i == (i << 5) - i;
4.2.3 重写hashCode的作用
  • 关于是否要同时重写equals()和hashCode()的问题,我的答案是:如果重写了equals最好重写hashCode,这几乎是默认的规则
  • 对于我们经常使用的比如String 、Math、Integer、Double等类,都进行了equals()和hashCode()方法的重写
  • hashCode()方法主要用于hash表中,比如HashSet、HashMap、HashTable等,在集合查找时,hashcode能极大的降低对象比较次数,提高查找效率
  • 集合Set中的元素是无序不可重复的,那么如何确保存入的元素不重复呢?逐个调用equals()方法进行比较?数据量少的时候还可以,但数据量大了时间复杂度基本上是O(n),会出现性能问题。
  • Java中采用哈希算法来解决这个问题,将对象(或数据)依特定算法直接映射到一个地址上,这样时间复杂度趋于O(1),对象的存取效率大大提高。
  • 因此,集合Set添加某元素时,先调用hashCode()方法,定位到此元素实际存储位置,如果这个位置没有元素,说明是第一次存储;若此位置有对象存在,调用equals()进行比较,相等就舍弃此元素不存,不等,则散列到其他地址。
  • 上面的示例也说明了为什么equals()相等,则hashCode()必须相等,进而重写equals方法时,也要对hashCode()方法进行重写。
  • HashMap的基本处理机制与HashSet很类似,只不过底层的数据存储结构有所不同而已。

4.3 示例

例子

4.4 总结

  • 在集合中通常通过equals方法来比较对象(数据)是否相等,通过hashCode方法来解决大数据量时会发生的性能问题。
  • 在实践中我们很少使用Object对象来作为Map的key,也是因为如果Object对象的属性变了,会导致hashCode变化,进而可能会导致找不到对应值,而String是不可变的对象,作为key就很适合

参考:https://www.toutiao.com/i6796115708481634819
参考:https://blog.csdn.net/weixin_42036942/article/details/100982950

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还能坚持

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值