1. 程式人生 > >hashcode與==與equals詳解

hashcode與==與equals詳解

ArrayList有序,HashSet不允許重複資料

HashCode方法的作用:把集合分成若干個區域,根據不同的值放入不同的區域中,查詢時,算出hashcode值

         找到指定的區域。(必須集合是hash演算法,hashcode值才有價值)

記憶體洩露:物件一直不再使用卻存在記憶體中執行之後沒有釋放,就產生記憶體洩露

public static void main(String[] args) {
  // TODO Auto-generated method stub
  Collection collection = new HashSet();
  ReflectPoint pt1 = new ReflectPoint(3, 3);
  ReflectPoint pt2 = new ReflectPoint(3, 5);
  ReflectPoint pt3 = new ReflectPoint(3, 3);
  collection.add(pt1);
  collection.add(pt2);
  collection.add(pt3);
  collection.add(pt1);
  
  pt1.y=7;//內容修改了,hashcode發生變化,存在另外一個記憶體空間,remove在原來的記憶體中找不到,,導致記憶體洩露
  collection.remove(pt1);//記憶體洩露
  
  System.out.println(collection.size());
 }

hashCode方法可以這樣理解:它返回的就是根據物件的記憶體地址換算出的一個值。這樣一來,當集合要新增新的元素時,先呼叫這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就呼叫它的equals方法與新元素進行比較,相同的話就不存了,不相同就雜湊其它的地址。這樣一來實際呼叫equals方法的次數就大大降低了,幾乎只需要一兩次。

我們還應該注意,Java語言對equals()的要求如下,這些要求是必須遵循的:
.1) 對稱性:如果x.equals(y)返回是“true”,那麼y.equals(x)也應該返回是“true”。
.2) 反射性:x.equals(x)必須返回是“true”。
.3) 類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那麼z.equals(x)也應該返回是“true”。
.4) 還有一致性:如果x.equals(y)返回是“true”,只要x和y內容一直不變,不管你重複x.equals(y)多少次,返回都是“true”。
.5) 任何情況下,x.equals(null),永遠返回是“false”;x.equals(和x不同型別的物件)永遠返回是“false”。
以上這五點是重寫equals()方法時,必須遵守的準則,如果違反會出現意想不到的結果,請大家一定要遵守

(3)其次,hashcode() 方法,在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;
}
(4)談到hashcode()和equals()就不能不說到hashset,hashmap,hashtable中的使用,具體是怎樣呢,請看如下分析:
Hashset是繼承Set介面,Set介面又實現Collection介面,這是層次關係。那麼hashset是根據什麼原理來存取物件的呢?
在hashset中不允許出現重複物件,元素的位置也是不確定的。在hashset中又是怎樣判定元素是否重複的呢?判斷兩個物件是否相等的規則是:
.1),判斷兩個物件的hashCode是否相等
如果不相等,認為兩個物件也不相等,完畢,如果相等,轉入2
.2),判斷兩個物件用equals運算是否相等
如果不相等,認為兩個物件也不相等
如果相等,認為兩個物件相等(equals()是判斷兩個物件是否相等的關鍵)
為什麼是兩條準則,難道用第一條不行嗎?不行,因為前面已經說了,hashcode()相等時,equals()方法也可能不等,所以必須用第2條準則進行限制,才能保證加入的為非重複元素。

java中的資料型別,可分為兩類:

值型別是儲存在記憶體中的堆疊(以後簡稱棧),而引用型別的變數在棧中僅僅是儲存引用型別變數的地址,而其本身則儲存在堆中。 
==操作比較的是兩個變數的值是否相等,對於引用型變量表示的是兩個變數在堆中儲存的地址是否相同,即棧中的內容是否相同。 
equals操作表示的兩個變數是否是對同一個物件的引用,即堆中的內容是否相同。


1.基本資料型別,也稱原始資料型別。byte,short,char,int,long,float,double,boolean
  他們之間的比較,應用雙等號(==),比較的是他們的值。
2.複合資料型別(類)
  當他們用(==)進行比較的時候,比較的是他們在記憶體中的存放地址,所以,除非是同一個new出來的物件,他們的比較後的結果為true,否則比較後結果為false。 JAVA當中所有的類都是繼承於Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較物件的記憶體地址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而不再是比較類在堆記憶體中的存放地址了。
  對於複合資料型別之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基於他們在記憶體中的存放位置的地址值的,因為Object的equals方法也是用雙等號(==)進行比較的,所以比較後的結果跟雙等號(==)的結果相同。

 String s1 = new String("zhangsan");
      String s2 = new String("zhangsan");
      System.out.println(s1 == s2);// false
      System.out.println(s1.equals(s2));// true

二、String是一個特殊的包裝類資料。可以用:
String str = new String("abc");
使用new建立字串物件的步驟如下,每呼叫一次就會建立一個新的物件。
1. 首先在堆(不是常量池)中建立一個包含指定內容的字串物件,並將字串引用指向該物件。
2. 去字串常量池中檢視,是否有包含該內容的物件。
3. 若有,則將new出來的字串物件與字串常量池中內容相同的物件聯絡起來。
4. 若沒有,則在字串常量池中再建立一個包含該內容的字串物件,並將堆中的物件與字串常量池中新創建出來的物件聯絡起來。
字串特殊的記憶體機制帶來的好處,即是:只比較兩個字串聯絡的常量池中物件是否為同一個即可,不論字串大小,比較速度一樣。
String str = "abc";
1.先在棧中建立一個對String類的物件引用變數str
2.然後查詢堆中常量池裡有沒有存放"abc"
3.如果沒有,則將"abc"存放進常量池,並令str指向”abc”
4.如果已經有"abc"則直接令str指向“abc”。