1. “==”比較的是什么
- ==是一個(gè)運(yùn)算符
- 對(duì)于8種基本的數(shù)據(jù)類型,比較的是變量所對(duì)應(yīng)內(nèi)存存儲(chǔ)的數(shù)值
- 對(duì)于指向?qū)ο蟮淖兞?,比較的也是變量所對(duì)應(yīng)內(nèi)存存儲(chǔ)的數(shù)值(即指向的對(duì)象占用堆內(nèi)存的首地址),也就是比較變量是否指向同一個(gè)對(duì)象。
public class Test {
public static void main(String[] args) {
int a = 1, b = 1, c = 2;
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
System.out.println("a==b:" + (a == b));
System.out.println("a==c:" + (a == c));
System.out.println("s1==s2:" + (s1 == s2));
System.out.println("s1==s3:" + (s1 == s3));
}
}
輸出結(jié)果:
a==b:true
a==c:false
s1==s2:true
s1==s3:false
上述代碼中:
如果一個(gè)變量指向的數(shù)據(jù)是對(duì)象類型的,那么就涉及兩塊內(nèi)存,變量占用的棧內(nèi)存和對(duì)象占用的堆內(nèi)存。例如Object obj = new Object();變量obj是存儲(chǔ)在棧內(nèi)存,new Object()存儲(chǔ)在堆內(nèi)存,此時(shí),變量obj所對(duì)應(yīng)的內(nèi)存中存儲(chǔ)的數(shù)值就是對(duì)象占用的那塊內(nèi)存的首地址。
2. “equals”比較的是什么
- equals是根類Object中的方法
源代碼如下:
public boolean equals(Object obj) {
return (this == obj);
}
- equals方法是用于比較兩個(gè)獨(dú)立對(duì)象的內(nèi)容是否相同,需要為類重寫equals方法。
例如,對(duì)于下面的代碼:
String a=new String("foo");
String b=new String("foo");
兩條new語(yǔ)句創(chuàng)建了兩個(gè)對(duì)象,然后用a與b這兩個(gè)變量分別指向了其中一個(gè)對(duì)象,這是兩個(gè)不同的對(duì)象,它們的首地址是不同的,即a和b中存儲(chǔ)的數(shù)值是不相同的,所以,表達(dá)式a==b將返回false,而String類重寫了equals方法,兩個(gè)變量中的內(nèi)容是相同的。所以,表達(dá)式a.equals(b)將返回true。
在實(shí)際開發(fā)中,我們經(jīng)常要比較傳遞進(jìn)行來的字符串內(nèi)容是否等,
例如,
String input = "xxxxx";
input.equals("quit");
許多人稍不注意就使用==進(jìn)行比較了,記住,字符串的比較基本上都是使用equals方法。
如果一個(gè)類沒有自己定義equals方法,那么它將繼承Object類的equals方法,Object類的equals方法的實(shí)現(xiàn)代碼如下:
boolean equals(Object o){
return this==o;
}
總結(jié):
如果一個(gè)類沒有定義自己的的equals方法,它默認(rèn)的equals方法就是從Object繼承來的equals方法,就相當(dāng)于“==”操作符。也是在比較兩個(gè)變量指向的對(duì)象是否是同一對(duì)象,這時(shí)候使用equals和使用==會(huì)得到同樣的結(jié)果,如果你編寫的類希望能夠比較該類創(chuàng)建的兩個(gè)實(shí)例對(duì)象的內(nèi)容是否相同,那么你必須覆蓋equals方法,由你自己寫代碼來決定在什么情況即可認(rèn)為兩個(gè)對(duì)象的內(nèi)容是相同的。
String類中重寫的equals方法源碼:
public boolean equals(Object anObject) {
if (this == anObject) { //如果指向的是同一個(gè)地址,那內(nèi)容肯定相同
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;
}
}
可以看出
String類中重寫的equals()是按照下面順序判斷的:
- 先判斷地址是否相同,若想等執(zhí)行2,否則返回
false, - 然后判斷是否是String類型,否則返回
false, - 然后依次判斷每一個(gè)字符是否相等,全部相等返回
true,否則返回false,
3. “hashcode()”
- hashcode是Object的方法。
- 默認(rèn)情況下,Object中的hashCode() 返回對(duì)象的32位jvm內(nèi)存地址。也就是說如果對(duì)象不重寫該方法,則返回相應(yīng)對(duì)象的32為JVM內(nèi)存地址。
String類源碼中重寫的hashCode方法如下:
public int hashCode() {
int h = hash; //Default to 0 ### String類中的私有變量,
if (h == 0 && value.length > 0) { //private final char value[]; ### Sting類中保存的字符串內(nèi)容的的數(shù)組
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
String源碼中使用private final char value[];保存字符串內(nèi)容,因此String是不可變的。
下面的例子,沒有重寫ComHashcode類的···hashCode···方法,所以直接返回32位對(duì)象在JVM中的地址;Long類重寫了hashCode方法,返回計(jì)算出的hashCode數(shù)值,String重寫了hashCode方法,返回了相同的數(shù)值。
public class ComHashcode {
public static void main(String[] args) throws Exception {
ComHashcode a = new ComHashcode();
ComHashcode b = new ComHashcode();
System.out.println(a.hashCode()); // 366712642
System.out.println(b.hashCode()); // 1829164700
String s1="helloworld";
String s2="helloworld";
String s3=new String("helloworld");
System.out.println("s1:"+s1.hashCode()); //s1:-1524582912
System.out.println("s2:"+s2.hashCode()); //s2:-1524582912
System.out.println("s3:"+s3.hashCode()); //s3:-1524582912
Long num1 = new Long(8);
Long num2 = new Long(8);
System.out.println(num1.hashCode()); // 8
System.out.println(num2.hashCode()); // 8
}
}
總結(jié):
綁定。當(dāng)equals方法被重寫時(shí),通常有必要重寫hashCode方法,以維護(hù) hashCode方法的常規(guī)協(xié)定,協(xié)定聲明相等對(duì)象必須具有相等的哈希碼。
綁定原因。Hashtable實(shí)現(xiàn)一個(gè)哈希表,為了成功地在哈希表中存儲(chǔ)和檢索對(duì)象,用作鍵的對(duì)象必須實(shí)現(xiàn) hashCode 方法和 equals 方法。
equals()方法和hashCode()方法存在如下契約:
如果兩個(gè)對(duì)象是相等的,那么他們的hashCode必須是相同的。
如果兩個(gè)對(duì)象具有相同的hashCode,它們可以相等,也可以不相等。默認(rèn)。
==默認(rèn)比較對(duì)象在JVM中的地址。
hashCode 默認(rèn)返回對(duì)象在JVM中的存儲(chǔ)地址。
equal比較對(duì)象,默認(rèn)也是比較對(duì)象在JVM中的地址,同==
重寫equals()方法而沒有重寫hashcode()方法造成的錯(cuò)誤:
import java.util.HashMap;
public class Cat {
private String color;
public Cat(String color) {
this.color = color;
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Cat)) {
return false;
}
if (this == obj) {
return true;
}
return (this.color.equals(((Cat) obj).color));
}
public static void main(String[] args) {
Cat cat1 = new Cat("白色");
Cat cat2 = new Cat("黑色");
System.out.println(cat1.equals(cat2)); //false,因?yàn)橹貙懥薳quals方法,且他們的顏色不一樣,所以返回false。
HashMap<Cat, String> maps = new HashMap<>();
maps.put(cat1, "白色");
maps.put(cat2, "黑色");
System.out.println(maps.get(new Cat("白色"))); //null,因?yàn)闆]有重寫hashcode方法,所以在查找的時(shí)候計(jì)算的hashcode值不一樣,無法找到,所以返回null。
}
}
造成該錯(cuò)誤的原因就在于重寫equals方法后沒有重寫hashcode方法。
對(duì)于Java中所有類的超級(jí)父類java.lang.Object而言,其hashCode()的默認(rèn)實(shí)現(xiàn)是:對(duì)于不同的對(duì)象就返回不同的整型值。上述示例代碼中,Cat類沒有重寫Object的hashCode()方法。所以,這條代碼System.out.println(maps.get(new Cat("白色")));創(chuàng)建的Cat類和cat1的hasncode值不一樣,所以無法找到。
最后給出重寫hashcode()方法后的代碼:
import java.util.HashMap;
public class Cat {
private String color;
public Cat(String color) {
this.color = color;
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Cat)) {
return false;
}
if (this == obj) {
return true;
}
return (this.color.equals(((Cat) obj).color));
}
@Override
public int hashCode() {
return this.color.hashCode();
}
public static void main(String[] args) {
Cat cat1 = new Cat("白色");
Cat cat2 = new Cat("黑色");
System.out.println(cat1.equals(cat2)); // false
HashMap<Cat, String> maps = new HashMap<>();
maps.put(cat1, "白色");
maps.put(cat2, "黑色");
System.out.println(maps.get(new Cat("白色"))); // 白色
}
}