java hashmap浅析

    xiaoxiao2022-06-29  34

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

    static class Entry<K,V> implements Map.Entry<K,V> {          final K key;          V value;          Entry<K,V> next;          final int hash;            // 构造函数          Entry(int h, K k, V v, Entry<K,V> n) {              value = v;              next = n;              key = k;              hash = h;          }                    // 返回key          public final K getKey() {              return key;          }            // 返回value          public final V getValue() {              return value;          }            // 设置value          public final V setValue(V newValue) {          V oldValue = value;              value = newValue;              return oldValue;          }            // 是否相同          public final boolean equals(Object o) {              // 如果o不是Map.Entry的实例,那么肯定不相同了              if (!(o instanceof Map.Entry))                  return false;              // 将o转成Map.Entry              Map.Entry e = (Map.Entry)o;              // 得到key和value对比是否相同,相同则为true              Object k1 = getKey();              Object k2 = e.getKey();              if (k1 == k2 || (k1 != null && k1.equals(k2))) {                  Object v1 = getValue();                  Object v2 = e.getValue();                  if (v1 == v2 || (v1 != null && v1.equals(v2)))                      return true;              }              // 否则为false              return false;          }            // hashCode          public final int hashCode() {              return (key==null   ? 0 : key.hashCode()) ^                     (value==null ? 0 : value.hashCode());          }            // 返回String          public final String toString() {              return getKey() + "=" + getValue();          }            // 使用该方法证明该key已经在该map中          void recordAccess(HashMap<K,V> m) {          }            // 该方法记录该key已经被移除了          void recordRemoval(HashMap<K,V> m) {          }      }        // 添加一个新的桶来保存该key和value      void addEntry(int hash, K key, V value, int bucketIndex) {      // 保存对应table的值      Entry<K,V> e = table[bucketIndex];          // 然后用新的桶套住旧的桶,链表          table[bucketIndex] = new Entry<K,V>(hash, key, value, e);          // 如果当前size大于等于阈值          if (size++ >= threshold)              // 调整容量              resize(2 * table.length);      }        // 新建一个桶,该方法不需要判断是否超过阈值      void createEntry(int hash, K key, V value, int bucketIndex) {      Entry<K,V> e = table[bucketIndex];          table[bucketIndex] = new Entry<K,V>(hash, key, value, e);          size++;      }  

    看到源码就清楚了,其实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方法源码

     public V put(K key, V value) {           // 如果key为null使用putForNullKey来获取           if (key == null)               return putForNullKey(value);           // 使用hash函数预处理hashCode           int hash = hash(key.hashCode());           // 获取对应的索引           int i = indexFor(hash, table.length);           // 得到对应的hash值的桶,如果这个桶不是,就通过next获取下一个桶           for (Entry<K,V> e = table[i]; e != null; e = e.next) {               Object k;               // 如果hash相同并且key相同               if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {                   // 获取当前的value                   V oldValue = e.value;                   // 将要存储的value存进去                   e.value = value;                   e.recordAccess(this);                   // 返回旧的value                   return oldValue;               }           }              modCount++;           addEntry(hash, key, value, i);           return null;       }  

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

    转载请注明原文地址: https://ju.6miu.com/read-1124754.html

    最新回复(0)