原創(chuàng)在這里:http://yanhui.site/2017/09/09/JavaInterview%E2%80%94%E2%80%94equals%E5%92%8ChashCode%E5%92%8C==/
一、Java中的數據類型
基本數據類型
注釋:byte是 字節(jié),bit是 位
- 整形:byte、int、short、long (默認int)
byte 1 字節(jié) 8 位
int 4 字節(jié) 32位
short 2字節(jié) 16位
long 8字節(jié) 64位 - 浮點型:float、double (默認double)
float 4 字節(jié) 32 位
double 8字節(jié) 64位 - 字符類型 char :2字節(jié) 16位
- 布爾類型 boolean
引用數據類型
- 類
- 接口
- 數組
引用類型可以理解我我們申明一個變量,指向它所代表的對象,就是這個變量引用了這個對象,所以說是引用類型(reference type)。
二、==
== 是用來比較兩個數據是否相等。
這里需要說下數據在內存中的存儲,無論是基本數據類型還是引用類型,要存儲在內存中,都是以二進制碼的形式存儲在內存中,內存會開辟一塊空間。因此,在內存中的數據都有一個數據值和地址。
- 如果比較的是基本數據類型,比較的是他們的數值是否相等
- 如果比較的引用數據類型,比較的是他們的存值的地址是否相等
注意:比較的必須是相同的數據類型的兩個數據
//比如存在一個Person類
Person p= new Person("張三",23);
Person p1= new Person("張三",23);
//兩個基本類型的數據
int a=1;
int b=2;
if(a==b){//由于1!=2 所以輸出false
//dosomething
}
//比較引用類型,比較地址,那么很顯然,在堆內存中,new出來了兩塊區(qū)域
//這兩塊的區(qū)域的地址顯然是不同的,因此輸出為false
if(p1==p2){ //false
//dosomething
}
== 我們基本已經知道了它的作用了,但是我們一般往HashMap中存入引用數據時,我們不想知道這個數據的地址是否已經存在,我們只想知道這個數據是否存在。如果存在我們get(key)取出來,供我們后續(xù)使用,如果不存在,我再存入。這時equals()就應運而生了。
三、equals()
equals()方法是Object中的一個方法,也就是萬物基類中的方法,那么也就是說每一個類中都會有這個方法。
public class Person {
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
}
public class Main5Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);
Person p1 = new Person("張三", 18);
Person p2 = new Person("張三", 18);
boolean b = p1 == p2;
boolean b1 = p1.equals(p2);
Log.e("比較結果b", "=" + b);
Log.e("比較結果b1", "=" + b1);
}
}
//結果:false false
到這里有疑惑了,equals()方法不是比較的內容嗎,那我的理解是p1==p2返回false,p1.equals(p2)應該返回true,他們內容相同啊。那我們來看下Object類下的equals源碼。
public boolean equals(Object obj) {
return (this == obj);
}
從源碼我可以知道,如果是引用類型,還是比較的地址,所以兩個對象的地址不相等,返回false沒有毛病。
所以我們在我們的類中一般都要重寫equals方法,自己比較內容是否相同,就拿剛才的Person類來說,如下:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//重寫equals方法,AS幫我們實現了, 比較內容是否相同
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
}
四、hashCode()
說到hashCode()方法,大家都知道他是用來返回hash值的方法。那什么是hash值,他又有什么作用呢?其實我也不是很清楚,我目前的理解是他就是類似于一個對象的屬性,那他是干什么用的呢?hash值是在HashMap、HashTable、HashSet的內部存取元素啊等相關操作時用到,目的就是提高他們的內部性能。
好了,大體了解了hash值之后,你還需要知道這樣一個觀念。Android系統中的類都是有關聯的,他們是互相約束配合工作的。比如萬物之基類的Object類中有許多方法,有的方法使用final修飾的,子類不能重寫。有的是沒有用final修飾的,子類可以重寫。那么子類就可以隨意重寫嗎?不是的,Object類是有規(guī)范約束的,意思就是如果你要重寫我的方法,必須要遵守一二三等等幾條規(guī)定,如果你不按規(guī)定辦事,可能就會影響到他人了。
了解了上面的概念后,就要說下Object類中,針對equals()方法,它定了哪些規(guī)定呢。
Object規(guī)范:在復寫equals方法時,你必須遵守下面的約定。
- 自反性: 對于任何非null的引用值x,x.equals(x)必須返回true。
- 對稱性:對于任何非null的引用值x和y,當且僅當x.equals(y)返回true時,那么y.equals(x)必須返回true。
- 傳遞性:對于任何非null的引用值x、y、z,如果x.equals(y)返回true,y.equals(z)也返回true,那么x.equals(z)必須返回true。
- 一致性:對于任何非null的引用值x和y,只要equals的比較操作時,對象中的所有信息沒有被修改,多次調用x.equlas(y)就會一直的返回true,或者一致的返回false。
對于任何非null的引用值x,x.equals(null)必須返回false。
Object規(guī)范:在復寫hashCode方法時,你必須遵守下面的約定。
- 在應用程序運行期間,只用對象的equals方法比較操作所用到的信息沒有被修改,那么對同一個對象多次調用,hashCode方法都必須始終如一的返回同一個數。在同一個程序的多次執(zhí)行過程中,每次執(zhí)行所返回的整數可以不一致。
- 如果兩個對象根絕equals方法比較是相等的,那么調用兩個對象的任意一個對象的hashCode方法都必須產生同樣的整數結果。
- 如果兩個對象根據equals方法比較不相等,那么調用兩個對象中任意一個對象的hashCode方法,則不一定產生不同的整數結果。但是程序員應該知道,給不相等的對象產生不同的整數結果,有可能提高散列表的性能。
知道這兩條約定了,那么我們還要知道一個非常重要的知識點。就是一個類中如果復寫了equals方法,必須也要復寫hashCode方法。
那我們?yōu)樯兑獜蛯慼ashCode方法呢?這個就是約定俗成的東西,如果你不這樣做就會產生錯誤,產生啥錯誤呢,我們來看下:
Person p1 = new Person("張三", 18);
Person p2 = new Person("張三", 18);
我們還是用上面的Person類,只復寫了equals方法,沒有復寫hashCode方法。根據hashCode的第二條約定,如果兩個對象的equals方法相等,那么hashCode必須返回相同的整數。現在我們的p1.equals(p2)返回true,但是hashCode就返回的是相同的整數嗎?
int h1 = p1.hashCode();
int h2 = p2.hashCode();
我們可以打印出兩個hashcode值來看下,結果是不一定相等。如果不相等就違背了第二條約定。
HashMap hashMap = new HashMap();
hashMap.put(p1,"張三");
我們再來看下,我往hashmap中存了了一個string類型的value值,而鍵是類型是Person的。我的期望是如果我用 hashMap.get(p2);,應該返回的是 張三,但是我們可以試下,發(fā)現返回的null。為什么會返回null呢,那是因為hashmap在取值的時候,要先看有沒有這個key值。找個這個key值的時候,他先用這個key值的hash值比較,如果hash值不相等,直接返回null,即沒有找到。如果key值的hash值相同,hashmap會繼續(xù)比較key值的內容是否都相同,如果相同說明找到了,如果不同返回null,說明沒找到。
通過上面的兩個例子,我們知道了為啥復寫equals方法時必須同時復寫hashCode方法。