HashMap 1.7工做原理

HashMap 数组

hashmap本质数据加链表。根据key取得hash值,而后计算出数组下标,若是多个key对应到同一个下标,就用链表串起来,新插入的在前面。ide

************************再强调一点,两个String的hashCode相同并不表明着equals比较时会相等,他们二者之间是没有必然关系************************大数据

看3段重要代码摘要:    this

public HashMap(int initialCapacity, float loadFactor) {
   int capacity = 1;
   while (capacity < initialCapacity)
      capacity <<= 1;

   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

有3个关键参数:
capacity:容量,就是数组大小
loadFactor:比例,用于扩容
threshold:=capacity*loadFactor   最多容纳的Entry数,若是当前元素个数多于这个就要扩容(capacity扩大为原来的2倍)指针

    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算hash值,再根据hash值取得数组下标,经过数组下标取出链表,遍历链表用equals取出对应key的value。ci

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

从数组(经过hash值)取得链表头,而后经过equals比较key,若是相同,就覆盖老的值,并返回老的值。(该key在hashmap中已存在)get

不然新增一个entry,返回null。新增的元素为链表头,之前相同数组位置的挂在后面。hash

另外:modCount是为了不读取一批数据时,在循环读取的过程当中发生了修改,就抛异常it

  if (modCount != expectedModCount)
     throw new ConcurrentModificationException();


         

下面看添加一个map元素io

    void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }


新增后,若是发现size大于threshold了,就resize到原来的2倍

    void resize(int newCapacity) {
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

新建一个数组,并将原来数据转移过去

void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }


将原来数组中的链表一个个取出,而后遍历链表中每一个元素,从新计算index并放入新数组。每一个处理的也放链表头。

在取出原来数组链表后,将原来数组置空(为了大数据量复制时更快的被垃圾回收?)

还有两点注意:

static class Entry<K,V> implements Map.Entry<K,V>是hashmap的静态内部类,iterator之类的是内部类,由于不是每一个元素都须要持有map的this指针。

HashMap把  transient Entry[] table;等变量置为transient,而后override了readObject和writeObject,本身实现序列化。  

相关文章
相关标签/搜索