概述
==: 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
equals:用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,String中的类是重写了object类里的equal()方法,而在原本的object类中,返回的是==的判断,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法。
hashCode : Object 的 native方法 , 获取对象的哈希值,用于确定该对象在哈希表中的索引位置,它实际上是一个int型整数。
关系运算符==
基本数据类型
在Java中有八种基本数据类型:
-
浮点型:float(4 字节), double(8 字节)
-
整型:byte(1 字节), short(2 字节), int(4 字节) , long(8 字节)
-
字符型: char(2 字节)
-
布尔型: boolean
对于这八种基本数据类型的变量,变量直接存储的是“值”,基本数据类型之间的比较需要用双等号(==),因为他们比较的是值
引用数据类型
接口、类、数组等非基本数据类型
Java中的字符串String属于引用数据类型。因为String是一个类
引用类型的变量存储的并不是“值”本身,而是与其关联的对象在内存中的地址,当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。因为每new一次就会重新开辟一个新的堆内存空间
String s1 = "小新";
String s2 = "小新";
System.out.println(s1 == s2); //true
String str1 = new String("小新");
String str2 = new String("小新");
System.out.println(s1 == str1); //false
System.out.println(str1 == str2); //false
s1 == s2为true,是因为s1和s2都是字符串字面值"小新"的引用,指向同一块地址,所以相等。
s1== str1为false,是因为通过new产生的对象在堆中,str1是堆中变量的引用,而s1是指向字符串字面值"小新"的引用,地址不同所以不相等。
str1 == str2为flase,是因为即使内容相同,由于不是指向同一个对象,也会返回false。
小结
- 若操作数的类型是基本数据类型,则该关系操作符判断的是左右两边操作数的值是否相等
- 若操作数的类型是引用数据类型,则该关系操作符判断的是左右两边操作数的内存地址是否相同。也就是说,若此时返回true,则该操作符作用的一定是同一个对象。
equals
来源
equals,是Objec类的方法,源代码如下:
public boolean equals(Object obj) {
return (this == obj);
}
可见Objec的equals方法,直接调用==,比较对象地址,即是否指向同一个对象。
不同的子类,可以重写此方法,进行两个对象的equals的判断。
String类源码中重写的equals方法如下:
public boolean equals(Object anObject) {
if (this == anObject) { // 先判断是否为同一对象
return true;
}
if (anObject instanceof String) { // 再判断类型是否一致,
// 最后判断内容是否一致.
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
使用equals方法,内部实现分为三个步骤:
- 先比较引用是否相同(是否为同一对象),
- 再判断类型是否一致(是否为同一类型),
- 最后比较内容是否一致
equals 重写原则
- 自反型,对于任何非null的引用值x,也就是自己等于自己x.equals(x)是true
- 对称性,对于任何非null的引用值x和y,也就是x.equals(y)等于y.equals(x)
- 传递性,对于任何非null的引用值x,y和z,如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么x.equals(z)也应该返回是“true”
- 一致性,对于任何非null的引用值x和y,多次调用x.equals(y)得到的结果是一样的,只要引用没有被修改
- 非空性,所有的对象都不能为空,对于任意非空引用x,x.equals(null)应该返回false。
小结
因此,对于 equals 方法:
- Object类中equals方法本意是比较两个对象是否相同
- 必要的时候,我们需要重写该方法,避免违背本意,且要遵循上述原则
hashCode
来源
hashCode 方法是基类Object中的 实例native方法,因此对所有继承于Object的类都会有该方法。
他的源码如下:
public native int hashCode();
说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法,比如String、Integer、Double等这些类都是覆盖了hashcode()方法的。例如在String类中定义的hashcode()方法如下:
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31 * h + val[off++];
}
hash = h;
}
return h;
}
想要弄明白hashCode的作用,必须要先知道Java中的集合。
总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,初学者可以简单理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
hashCode()和equals()关系
- equals()相等的两个对象,hashcode()一定相等;
- hashcode()相等,那么equals()有可能相等,也有可能不等 ;
- 如果hashcode()不等,equals()一定不相等;
- 一般来讲,equals 这个方法是给用户调用的,而 hashcode 方法一般用户不会去调用 ;
- 当一个对象类型作为集合对象的元素时,那么这个对象应该拥有自己的equals()和hashCode()设计,而且要遵守前面所说的几个原则。
为什么重写equals的同时也要重写hashCode?
1.使用hashcode方法提前校验,可以避免每一次比对都调用equals方法,提高效率
2.保证是同一个对象,如果重写了equals方法,而没有重写hashcode方法,会出现equals相等的对象,hashcode不相等的情况,重写hashcode方法就是为了避免这种情况的出现。
小结
- hashcode是系统用来快速检索对象而使用
- equals方法本意是用来判断引用的对象是否一致
- 重写equals方法和hashcode方法时,equals方法中用到的成员变量也必定会在hashcode方法中用到,只不过前者作为比较项,后者作为生成摘要的信息项,本质上所用到的数据是一样的,从而保证二者的一致性
参考资料
https://blog.csdn.net/justloveyou_/article/details/52464440#commentBox
https://www.cnblogs.com/aspirant/p/7079538.html