• 【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.

  • 相关阅读:
    青蛙学Linux—软件安装
    青蛙学Linux—文本编辑器Vi/Vim
    linux下安装java
    anaconda 换源
    origin从图中获得数据
    endnote X7参考文献缩进设置
    endnote X7 加入文献
    endnote X7使用方法
    linux下安装openmpi
    origin添加error bar
  • 原文地址:https://www.cnblogs.com/dream-to-pku/p/6268119.html
Copyright © 2020-2023  润新知