【jdk源码学习】HashMap

 1 package com.emsn.crazyjdk.java.util;  
 2   
 3 /** 
 4  * “人”类,重写了equals和hashcode方法...,以id来区分不同的人,你懂的... 
 5  *  
 6  * @author emsn1026 
 7  * 
 8  */  
 9   
10 public class Person {  
11       
12     /** 
13      * 身份id 
14      */  
15     private String id;  
16       
17     /** 
18      * 姓名 
19      */  
20     private String name;  
21   
22     public String getId() {  
23         return id;  
24     }  
25   
26     public void setId(String id) {  
27         this.id = id;  
28     }  
29   
30     public String getName() {  
31         return name;  
32     }  
33   
34     public void setName(String name) {  
35         this.name = name;  
36     }  
37   
38     @Override  
39     public int hashCode() {  
40         final int prime = 31;  
41         int result = 1;  
42         result = prime * result + ((id == null) ? 0 : id.hashCode());  
43         return result;  
44     }  
45   
46     @Override  
47     public boolean equals(Object obj) {  
48         if (this == obj)  
49             return true;  
50         if (obj == null)  
51             return false;  
52         if (getClass() != obj.getClass())  
53             return false;  
54         Person other = (Person) obj;  
55         if (id == null) {  
56             if (other.id != null)  
57                 return false;  
58         } else if (!id.equals(other.id))  
59             return false;  
60         return true;  
61     }  
62   
63     @Override  
64     public String toString() {  
65         return "Person [id=" + id + ", name=" + name + "]";  
66     }  
67       
68 }  

新建一个Person类,重写其中的equals和hashcode方法。这样,同样id的人会被认为是同样的事例,不同id的即时姓名相同也是不同的人,把Person类的实例作为HashMap的key时,key的唯一性讲通过Person实例的id来控制.

package com.emsn.crazyjdk.java.util;  
  
import java.util.HashMap;  
import java.util.Map;  
import java.util.Map.Entry;  
  
import com.emsn.crazyjdk.java.util.Person;  
  
/** 
 * @author emsn1026 
 * 
 */  
public class MapTest {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
            Map m = new HashMap();  
            Person p1 = new Person();  
            Person p2 = new Person();  
              
            p1.setId("1");  
            p1.setName("name1");  
            p2.setId("1");  
            p2.setName("name2");  
                      
            m.put(p1, "person1");  
            m.put(p2, "person2");  
              
            System.out.println("Map m's size :" + m.size());  
              
            for(Object o :m.entrySet()){  
                Entry e = (Entry)o;  
                System.out.println("key:"+ e.getKey());  
                System.out.println("value:"+ e.getValue());  
            }  
              
        }  
  
}  

打印的结果是 
Map m's size :1 
key:Person [id=1, name=name1] 
value:person2 

可见key已存在,value被覆盖,这个结果可以预测。那么接下来我们把代码修改下: 

package com.emsn.crazyjdk.java.util;  
  
import java.util.HashMap;  
import java.util.Map;  
import java.util.Map.Entry;  
  
import com.emsn.crazyjdk.java.util.Person;  
  
/** 
 * @author emsn1026 
 * 
 */  
public class MapTest {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
            Map m = new HashMap();  
            Person p1 = new Person();  
            Person p2 = new Person();  
              
            p1.setId("1");  
            p1.setName("name1");  
            p2.setId("2");  
            p2.setName("name2");  
                      
            m.put(p1, "person1");  
            m.put(p2, "person2");  
  
                        System.out.println("Map m's size :" + m.size());  
              
            p2.setId("1");  
              
            System.out.println("Map m's size :" + m.size());  
              
            for(Object o :m.entrySet()){  
                Entry e = (Entry)o;  
                System.out.println("key:"+ e.getKey());  
                System.out.println("value:"+ e.getValue());  
            }  
              
        }  
  
}  

 此处的变化是将p1,p2的id设成不同,然后都作为key插入map,因为两个key不相同,所以我们的预测是都可以插入,此时map的size应该为2,待插入后我们修改p2的id为1,即与p1相同,这样就造成了两个entry的key相同的情况,测试再查看map的结构,看看是不是还是刚才插入的两项。 
    此时我们不知道HashMap的内部实现,所以我们不知道它的实例会不会在数据插入后还继续维持key的唯一性。 
    我们可以猜测的是三种答案: 
    1.抛出异常,不允许修改p2的id与p1相同,维护key的唯一性; 
    2.可以修改,但根据某种算法删除p1或p2中的一项,也能起到维护key的唯一性; 
    3.可以修改,没有任何事情发生....两项id相同的person实例并存于map中,即存在同一个key对应了两个value。 

    那么各位在没尝试并且没有查看过HashMap的源代码时会做出怎样的选择呢? 

 结果打印如下: 

Map m's size :2 
key:Person [id=1, name=name2] 
value:person2 
key:Person [id=1, name=name1] 
value:person1 

    那么是预测的第三种情况...这原本不是我最看好的答案..这样我就有一个疑问了,既然可以有两个相同的key对应不同的value存在,那么我通过这个key应该拿到的value是哪个呢?在上述代码的main方法末尾加入以下两行代码: 

 System.out.println("Map m 通过get方法用key p1:"+p1+"时,获取的value:"+m.get(p1));  
System.out.println("Map m 通过get方法用key p2:"+p2+"时,获取的value:"+m.get(p2));  

得到的结果如下: 

Map m 通过get方法用key p1:Person [id=1, name=name1]时,获取的value:person1 
Map m 通过get方法用key p2:Person [id=1, name=name2]时,获取的value:person1 

可见不论你使用p1还是p2,得到的value都是person1。 

/**
    *jdk中get方法的源码
     * Returns the value to which the specified key is mapped in this identity
     * hash map, or <tt>null</tt> if the map contains no mapping for this key.
     * A return value of <tt>null</tt> does not <i>necessarily</i> indicate
     * that the map contains no mapping for the key; it is also possible that
     * the map explicitly maps the key to <tt>null</tt>. The
     * <tt>containsKey</tt> method may be used to distinguish these two cases.
     *
     * @param   key the key whose associated value is to be returned.
     * @return  the value to which this map maps the specified key, or
     *          <tt>null</tt> if the map contains no mapping for this key.
     * @see #put(Object, Object)
     */
    public V get(Object key) {
if (key == null)
    return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;//--关键在这里。
        }
        return null;
    }

如果一个Key对应2个Value。 他按顺序找到后,直接就 Return a.value了。而不会循环Person2.

原文地址:https://www.cnblogs.com/dream-to-pku/p/6268119.html