`
Heis
  • 浏览: 112291 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

大话深入浅出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到链表中。

<meta name="ProgId" content="Word.Document"> <meta name="Generator" content="Microsoft Word 11"> <meta name="Originator" content="Microsoft Word 11"> <link rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

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  
0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics