java hashmap浅析

hashmap是基于hash算法实现的一个存储键值对的大型hash表,他本质上是一个entry类的数组,entry[],entry是hashmap的内部类,之所有使用内部类,是因为内部类可以使用所有的外部类所有变量,并且只用于外部类,期望对其他类不可见。我们来看一看entry的源码

  1. static class Entry implements Map.Entry {  
  2.         final K key;  
  3.         V value;  
  4.         Entry next;  
  5.         final int hash;  
  6.   
  7.         // 构造函数  
  8.         Entry(int h, K k, V v, Entry n) {  
  9.             value = v;  
  10.             next = n;  
  11.             key = k;  
  12.             hash = h;  
  13.         }  
  14.           
  15.         // 返回key  
  16.         public final K getKey() {  
  17.             return key;  
  18.         }  
  19.   
  20.         // 返回value  
  21.         public final V getValue() {  
  22.             return value;  
  23.         }  
  24.   
  25.         // 设置value  
  26.         public final V setValue(V newValue) {  
  27.         V oldValue = value;  
  28.             value = newValue;  
  29.             return oldValue;  
  30.         }  
  31.   
  32.         // 是否相同  
  33.         public final boolean equals(Object o) {  
  34.             // 如果o不是Map.Entry的实例,那么肯定不相同了  
  35.             if (!(o instanceof Map.Entry))  
  36.                 return false;  
  37.             // 将o转成Map.Entry  
  38.             Map.Entry e = (Map.Entry)o;  
  39.             // 得到key和value对比是否相同,相同则为true  
  40.             Object k1 = getKey();  
  41.             Object k2 = e.getKey();  
  42.             if (k1 == k2 || (k1 != null && k1.equals(k2))) {  
  43.                 Object v1 = getValue();  
  44.                 Object v2 = e.getValue();  
  45.                 if (v1 == v2 || (v1 != null && v1.equals(v2)))  
  46.                     return true;  
  47.             }  
  48.             // 否则为false  
  49.             return false;  
  50.         }  
  51.   
  52.         // hashCode  
  53.         public final int hashCode() {  
  54.             return (key==null   ? 0 : key.hashCode()) ^  
  55.                    (value==null ? 0 : value.hashCode());  
  56.         }  
  57.   
  58.         // 返回String  
  59.         public final String toString() {  
  60.             return getKey() + "=" + getValue();  
  61.         }  
  62.   
  63.         // 使用该方法证明该key已经在该map中  
  64.         void recordAccess(HashMap m) {  
  65.         }  
  66.   
  67.         // 该方法记录该key已经被移除了  
  68.         void recordRemoval(HashMap m) {  
  69.         }  
  70.     }  
  71.   
  72.     // 添加一个新的桶来保存该key和value  
  73.     void addEntry(int hash, K key, V value, int bucketIndex) {  
  74.     // 保存对应table的值  
  75.     Entry e = table[bucketIndex];  
  76.         // 然后用新的桶套住旧的桶,链表  
  77.         table[bucketIndex] = new Entry(hash, key, value, e);  
  78.         // 如果当前size大于等于阈值  
  79.         if (size++ >= threshold)  
  80.             // 调整容量  
  81.             resize(2 * table.length);  
  82.     }  
  83.   
  84.     // 新建一个桶,该方法不需要判断是否超过阈值  
  85.     void createEntry(int hash, K key, V value, int bucketIndex) {  
  86.     Entry e = table[bucketIndex];  
  87.         table[bucketIndex] = new Entry(hash, key, value, e);  
  88.         size++;  
  89.     }  

看到源码就清楚了,其实map其实是用的entry类的变量key,value分别用来存储键值对,然后next用于存储hash值相同的entry对象。看了源码这么久,我觉得hashmap最精彩的一部分就是利用key的hashcode来规定entry对象在entry[]里的位置,当两个key的hashcode相同时,当前数组的位置由于存储最新的entry类,然后用next存储老的entry,而这个老的entry类的next变量又可能存储一个entry类,这样不断递归就形成了链表。

hashmap重写了超类object中的hashmap,equal,tostring方法。因为key值是唯一的,而相同key的hashcode的值可能不同,需要我们重写.

我们再来看一看hashmap的put方法源码

  1.  public V put(K key, V value) {  
  2.         // 如果key为null使用putForNullKey来获取  
  3.         if (key == null)  
  4.             return putForNullKey(value);  
  5.         // 使用hash函数预处理hashCode  
  6.         int hash = hash(key.hashCode());  
  7.         // 获取对应的索引  
  8.         int i = indexFor(hash, table.length);  
  9.         // 得到对应的hash值的桶,如果这个桶不是,就通过next获取下一个桶  
  10.         for (Entry e = table[i]; e != null; e = e.next) {  
  11.             Object k;  
  12.             // 如果hash相同并且key相同  
  13.             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
  14.                 // 获取当前的value  
  15.                 V oldValue = e.value;  
  16.                 // 将要存储的value存进去  
  17.                 e.value = value;  
  18.                 e.recordAccess(this);  
  19.                 // 返回旧的value  
  20.                 return oldValue;  
  21.             }  
  22.         }  
  23.   
  24.         modCount++;  
  25.         addEntry(hash, key, value, i);  
  26.         return null;  
  27.     }  

                  hashmap新增一个键值对时,先计算key的hashcode值计算它在entry[]数组中的位置,如果位置没有被占用,直接把entry放在这个位置,如果这个位置上已经有值了,然后把当前位置的entry类的key,value变量值变为新添加的键值对,而next的entry放置老的key,value,而next再次放下一个的next的entry类的key,value,通过for循环逐渐附值。

你可能感兴趣的:(java hashmap浅析)