Heis的Blog

          保持簡單,保持愚蠢
          隨筆 - 29, 文章 - 1, 評論 - 122, 引用 - 0
          數據加載中……

          大話深入淺出Effective Java核心實戰編程思想之——那些雞翅

              好吧好吧,我承認這有點標題黨的嫌疑,我這不是隔太久沒更新,有點興奮么。
              板磚拍夠了,臭雞蛋扔夠了,別來打醬油便行了。我這就進入正題。其實正確的標題應該叫Effective Java讀書心得之雞翅的故事。

              關于雞翅的故事,相傳最近最近以前……
           1 import static org.junit.Assert.*;
           2 
           3 import java.util.HashMap;
           4 
           5 import org.junit.Test;
           6 
           7 
           8 public class TestObjectHashCode {
           9     
          10     @Test
          11     public void testIt(){
          12           //最近,麥當當推出了麥香翅,味道不是一般的好,那是相當的好。
          13           //于是受到消費者的青睞,大受歡迎。
          14           雞翅 麥香翅=new 雞翅("麥香翅");
          15           味道 味道好極了=new 味道("味道好極了");
          16           HashMap 市場=new HashMap();
          17           市場.put(麥香翅, 味道好極了);
          18           
          19           //這一切都被一個山寨小食店看在眼里,他們決定打著麥香翅的名號,推出實際上味道一般的山寨麥香翅
          20           雞翅 山寨麥香翅=new 雞翅("麥香翅");            
          21           
          22           //山寨小食店的師傅還是很有智慧的,他們通過某某方式,通過ISO叉叉叉叉的認證。
          23           //他們很天真地認為他們的山寨翅可以媲美麥香翅。
          24           assertTrue(山寨麥香翅.equals(麥香翅)); 
          25           
          26           //但是結果大家都知道了,山寨翅并沒有獲得市場的認可,魚目混珠終究被市場識別出來。
          27           assertFalse(味道好極了.equals(市場.get(山寨麥香翅)));
          28           
          29           //山寨小食店苦思瞑想,終于發現了問題,原來他們指打相同的名號是不行的。
          30           //他們的并沒有山寨出麥香翅代號為HashCode的秘制醬料
          31           assertFalse(麥香翅.hashCode()==山寨麥香翅.hashCode());         
          32     }
          33     
          34     public static final class 味道{
          35           private String description;
          36 
          37           public 味道(String des){
          38                 description=des;
          39           }
          40           
          41           public String getDescription() {
          42                 return description;
          43           }
          44 
          45           public void setDescription(String aDes) {
          46                 this.description = aDes;
          47           }
          48           
          49     }
          50 
          51     public static final class 雞翅 {
          52 
          53         private String Name;
          54 
          55         public 雞翅(String name){
          56               this.Name=name;
          57         }
          58         
          59         public String getName() {
          60               return Name;
          61         }
          62 
          63         public void setName(String aName) {
          64               this.Name = aName;
          65         }
          66         
          67         @Override
          68         public boolean equals(Object obj){
          69               if(!(obj instanceof 雞翅)){
          70                     return false;
          71               }
          72               return ((雞翅)obj).getName().equals(this.Name);
          73         }
          74   }
          75 
          76 }
          77 
              看完了不知道我想說啥?看來沒認真看《Effective Java》嘛,這本書可是每個java程序員進階必修之書,沒看過真不能自稱大蝦級別。好了,這里說到了正題的正題,其實我想說的是:

          《Effective Java》第八條:改寫equals時總是要改寫hashCode。


          *為什么要改寫hashCode
          答:(書上原話)“如果不這樣做的話,就會違反Object.hashCode的通用約定,從而導致該類無法與所有基于散列值(hash)的集合類在一起正常工作,這樣的集合類包括HashMap,HashSet和Hashtable.

          *那么為什么會導致該類無法與所有基于散列值(hash)的集合類在一起正常工作呢?
          答:且看一個HashMap的源代碼解釋。

          HashMap是通過一個叫table[]的數組來存取,table的每個元素是一個鏈表結構,鏈表的每個元素稱為 Entry<key,value>,是真正存放key和value的對象。在put的過程中,HashMap會通過特定的哈希算法將key對象的hashCode對應到table的某個索引下,然后再用key對比鏈表中每個Entry.key,如果key相同則更新value。否則就加入新的 Entry到鏈表中。

          HashMap的數據結構圖:




          再看一段HashMap的源代碼:
           1 public V put(K key, V value) {
           2     K k = maskNull(key);// 如果key為null則使用缺省的Object
           3         int hash = hash(k);//將k的hashCode經過一定的計算得到新的HashCode
           4         int i = indexFor(hash, table.length);//取得HashCode在table中的位置
           5 
           6       //首先在數組內根據key的HashCode找到Entry鏈表的第一個Entry        
           7         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
           8         //如果Entry的key值HashCode相同,而且具有相同的引用或邏輯相等(equals)
           9             if (e.hash == hash && eq(k, e.key)) {
          10             //將新的value放入Entry中,返回舊的value
          11                 V oldValue = e.value;
          12                 e.value = value;
          13                 e.recordAccess(this);
          14                 return oldValue;
          15             }
          16         }
          17       
          18         //修改次數加1
          19         modCount++;
          20       //在table的第i個位置的鏈表后加上一個新的Entry
          21         addEntry(hash, k, value, i);
          22         return null;
          23 }
          24 
          25 static boolean eq(Object x, Object y) {
          26         return x == y || x.equals(y);
          27     }
          28 

          *最后來看java.lang.Object的約定(經過自己語言描述,原話自己看書去)
          1.如果一個類的equals方法所用到的信息(邏輯相等的條件因素)沒有被修改的話,那么它hashCode也不會改變。換個角度來看,也就是說,hashCode返回值最好與equals方法所用到的信息相關。

          2.如果兩個實例根據equals對比是相等的,那么它們的HashCode相等。

          3.如果兩個實例根據equals對比是不相等的,那么它們的HashCode最好是不等,這對于Hash性能的提高有好處。

          E.g 如果Person實例的ID屬性沒有被修改的話,那么它的HashCode也不會改變
           1 public class Person{
           2         private String ID;
           3         public boolean equals(Object obj){
           4              if(!(obj instanceof Person)&& ((Person)obj).getID()!=null){
           5                 return false;
           6         }
           7             return ((Person)obj).getID().equals(this.ID);
           8         }
           9 
          10         public int hashCode(){
          11             if(ID==null){
          12                 return 23;
          13             }else{
          14                 return ID.hashCode();
          15             }
          16         }
          17         public String getID() {
          18             return ID;
          19         }
          20         public void setID(String id) {
          21             ID = id;
          22         }
          23         
          24 }
          25 


          程序員的一生其實可短暫了,這電腦一開一關,一天過去了,嚎;電腦一開不關,那就成服務器了,嚎……

          posted on 2009-06-20 00:59 Heis 閱讀(1695) 評論(0)  編輯  收藏 所屬分類: Effective Java

          主站蜘蛛池模板: 陇西县| 大港区| 礼泉县| 石阡县| 离岛区| 衡阳市| 荆门市| 壤塘县| 南乐县| 仙桃市| 桐城市| 鸡东县| 临朐县| 汽车| 饶阳县| 平利县| 武城县| 庄浪县| 临朐县| 德昌县| 通辽市| 叙永县| 米泉市| 安义县| 罗定市| 营口市| 阳信县| 保靖县| 赤水市| 霍城县| 临夏市| 密云县| 保山市| 和静县| 旬阳县| 荆州市| 晋江市| 乌兰察布市| 阜宁县| 乌鲁木齐市| 噶尔县|