== 和 equals,equals 與 hashcode,HashSet 和 HashMap,HashMap 和 Hashtable

一:== 和 equals

== 比較引用的地址
equals 比較引用的內容 (Object 類本身除外)

String obj1 = new String("xyz");
String obj2 = new String("xyz");
// If String obj2 = obj1, the output will be true
if(obj1 == obj2)
System.out.printlln("obj1==obj2 is TRUE");
else
System.out.println("obj1==obj2 is FALSE");
// It will print obj1==obj2 is False
// If String obj2 = obj1, the output will be true

默認的, equals() 方法實際上和 “==” 在 object 類裏是一樣的. 但是這個方法在每一個子類裏都
會被覆寫用來比較引用的內容 (因爲每個類都繼承了 object 類並覆寫了這個方法)

String obj1 = new String("xyz");
String obj2 = new String("xyz");
if(obj1.equals(obj2))
System.out.printlln("obj1==obj2 is TRUE");
else
System.out.println("obj1==obj2 is FALSE");
Resultat: obj1==obj2 is TRUE

string1="aaa";

string2="aaa";

String string3=new String("aaa");

String string4=new String("aaa");

string1==string2 // true; .

string1.equals(string2);//true;

string3==string4;//false 因爲用new創建了2個對象,所以是兩個不同的內存地址

string3.equals(string4);//true 而String類的是不可改變的,所以會指向同一個內存地址,所以返回爲true

equals()是object的方法,所以只是適合對象,不適合於基本類型,equals()默認是用"=="比較兩個對象的內存地址,如果想要比較兩個對象的內容,要重寫equals()方法纔可...而==可以比較兩個基本類型,也可以是對象...

總而言之:在類對象中 equals()方法比較的是對象的值,==比較的是對象.即爲對象的引用(即爲內存地址)


二:equals 與 hashcode

如果需要比較對象的值,就需要equals方法了. 看一下JDK中equals方法的實現:

public boolean equals(Object obj) {
return (this == obj);
}
也就是說,默認情況下比較的還是對象的地址. 所以如果把對象放入Set中等操作,就需要重寫equals方法了
重寫之後的 equals() 比較的就是對象的內容了

在Java中任何一個對象都具備equals(Object obj)和hashcode()這兩個方法,因爲他們是在Object類中定義的。 
equals(Object obj)方法用來判斷兩個對象是否“相同”,如果“相同”則返回true,否則返回false。 
hashcode()方法返回一個int數,在Object類中的默認實現是“將該對象的內部地址轉換成一個整數返回”。 
接下來有兩個個關於這兩個方法的重要規範(我只是抽取了最重要的兩個,其實不止兩個): 
規範1:若重寫equals(Object obj)方法,有必要重寫hashcode()方法,確保通過equals(Object obj)方法判斷結果爲true的兩個對象具備相等的hashcode()返回值。說得簡單點就是:“如果兩個對象相同,那麼他們的hashcode應該 相等”。不過請注意:這個只是規範,如果你非要寫一個類讓equals(Object obj)返回true而hashcode()返回兩個不相等的值,編譯和運行都是不會報錯的。不過這樣違反了Java規範,程序也就埋下了BUG。 
規範2:如果equals(Object obj)返回false,即兩個對象“不相同”,並不要求對這兩個對象調用hashcode()方法得到兩個不相同的數。說的簡單點就是:“如果兩個對象不相同,他們的hashcode可能相同”。 
根據這兩個規範,可以得到如下推論: 
1、如果兩個對象equals,Java運行時環境會認爲他們的hashcode一定相等。 
2、如果兩個對象不equals,他們的hashcode有可能相等。 
3、如果兩個對象hashcode相等,他們不一定equals。 
4、如果兩個對象hashcode不相等,他們一定不equals。 

三:HashSet 和 HashMap

HashSet和HashMap一直都是JDK中最常用的兩個類,HashSet要求不能存儲相同的對象,HashMap要求不能存儲相同的鍵。 
那麼Java運行時環境是如何判斷HashSet中相同對象、HashMap中相同鍵的呢?當存儲了“相同的東西”之後Java運行時環境又將如何來維護呢?

根據上面的equals 與 hashcode分析我們就可以推斷Java運行時環境是怎樣判斷HashSet和HastMap中的兩個對象相同或不同了。我的推斷是:先判斷hashcode是否相等,再判斷是否equals。

什麼是HashSet?

HashSet實現了Set接口,它不允許集合中有重複的值,當我們提到HashSet時,第一件事情就是在將對象存儲在HashSet之前,要先確保對象重寫equals()和hashCode()方法,這樣才能比較對象的值是否相等,以確保set中沒有儲存相等的對象。如果我們沒有重寫這兩個方法,將會使用這個方法的默認實現。

public boolean add(Object o)方法用來在Set中添加元素,當元素值重複時則會立即返回false,如果成功添加的話會返回true。

什麼是HashMap?

HashMap實現了Map接口,Map接口對鍵值對進行映射。Map中不允許重複的鍵。Map接口有兩個基本的實現,HashMap和TreeMap。TreeMap保存了對象的排列次序,而HashMap則不能。HashMap允許鍵和值爲null。HashMap是非synchronized的,但collection框架提供方法能保證HashMap synchronized,這樣多個線程同時訪問HashMap時,能保證只有一個線程更改Map。

public Object put(Object Key,Object value)方法用來將元素添加到map中。

HashSet和HashMap的區別

*HashMap**HashSet*
HashMap實現了Map接口HashSet實現了Set接口
HashMap儲存鍵值對HashSet僅僅存儲對象(且無重複對象)
使用put()方法將元素放入map中使用add()方法將元素放入set中
HashMap中使用鍵對象來計算hashcode值HashSet使用成員對象來計算hashcode值,對於兩個對象來說hashcode可能相同,所以equals()方法用來判斷對象的相等性,如果兩個對象不同的話,那麼返回false
HashMap比較快,因爲是使用唯一的鍵來獲取對象HashSet較HashMap來說比較慢

四:HashTable和HashMap

HashMap和Hashtable都實現了Map接口,但決定用哪一個之前先要弄清楚它們之間的分別。主要的區別有:線程安全性同步(synchronization),以及速度

  1. HashMap幾乎可以等價於Hashtable,除了HashMap是非synchronized的,並可以接受null(HashMap allows one null key and any number of null values.,而Hashtable則不行)。這就是說,HashMap中如果在表中沒有發現搜索鍵,或者如果發現了搜索鍵,但它是一個空的值,那麼get()將返回null。如果有必要,用containKey()方法來區別這兩種情況。
  2. HashMap是非synchronized,而Hashtable是synchronized,這意味着Hashtable是線程安全的,多個線程可以共享一個Hashtable;而如果沒有正確的同步的話,多個線程是不能共享HashMa的。 即是說,在多線程應用程序中,不用專門的操作就安全地可以使用Hashtable了;而對於HashMap,則需要額外的同步機制。但HashMap的同步問題可通過Collections的一個靜態方法得到解決:
                Map Collections.synchronizedMap(Map m)
    這個方法返回一個同步的Map,這個Map封裝了底層的HashMap的所有方法,使得底層的HashMap即使是在多線程的環境中也是安全的。                                                                                              而而且
    Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。
  1. 另一個區別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以當有其它線程改變了HashMap的結構(增加或者移除元素),將會拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這並不是一個一定發生的行爲,要看JVM。這條同樣也是Enumeration和Iterator的區別。
  2. 由於Hashtable是線程安全的也是synchronized,所以在單線程環境下它比HashMap要慢。如果你不需要同步,只需要單一線程,那麼使用HashMap性能要好過Hashtable。
  3. HashMap不能保證隨着時間的推移Map中的元素次序是不變的。
  4. 哈希值的使用不同,HashTable直接使用對象的hashCode,代碼是這樣的:
          int hash = key.hashCode();
          int index = (hash & 0x7FFFFFFF) % tab.length;
    而HashMap重新計算hash值,而且用與代替求模:
    int hash = hash(k);
    int i = indexFor(hash, table.length);


要注意的一些重要術語:

1) sychronized意味着在一次僅有一個線程能夠更改Hashtable。就是說任何線程要更新Hashtable時要首先獲得同步鎖,其它線程要等到同步鎖被釋放之後才能再次獲得同步鎖更新Hashtable。

2) Fail-safe和iterator迭代器相關。如果某個集合對象創建了Iterator或者ListIterator,然後其它的線程試圖“結構上”更改集合對象,將會拋出ConcurrentModificationException異常。但其它線程可以通過set()方法更改集合對象是允許的,因爲這並沒有從“結構上”更改集合。但是假如已經從結構上進行了更改,再調用set()方法,將會拋出IllegalArgumentException異常。

3) 結構上的更改指的是刪除或者插入一個元素,這樣會影響到map的結構。

關注公衆號,分享乾貨,討論技術



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章