文章目录
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