美文网首页
HashMap实现原理

HashMap实现原理

作者: lesline | 来源:发表于2018-02-09 11:26 被阅读27次

    HashMap实现原理

    基本组成

    HashMap 由Entry数组组成,Entry下是链表(jdk1.8变成红黑树)。
    HashMap是基于hashing的原理,当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。

    如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?

    默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

    put过程:

    1. 先判断key是不是为空,为空做插入操作;
    2. 对key做hash操作,跟据hash结果定位数组下标,判断该下标的entry是否存在,如果存在,遍历链表查找key值相同的entry,并更新其value值,并返回旧值;如果不存在执行第3步新增entry操作;
    3. 新增时,判断数据(已插入entry的数量)长度是否大于等于12=16*0.75,如果是则resize操作。
      if ((size >= threshold) && (null != table[bucketIndex])) {
                resize(2 * table.length);
      }
    

    创建entry,插入链头

    你了解重新调整HashMap大小存在什么问题吗?

    当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。
    在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。
    如果条件竞争发生了,那么就死循环了。(同时调整理大小导致链环)
    我们看下过程:
    Entry的结构为:

    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
       static class Entry<K,V> implements Map.Entry<K,V> {
            final K key;
            V value;
            Entry<K,V> next;
            int hash;
    

    resize过程:

    当 ((size >= threshold) 才resize
    其中
    threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
    即:threshold=16*0.75=12时resize

     static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
     static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
      void addEntry(int hash, K key, V value, int bucketIndex) {
            if ((size >= threshold) && (null != table[bucketIndex])) {
                resize(2 * table.length);
                hash = (null != key) ? hash(key) : 0;
                bucketIndex = indexFor(hash, table.length);
            }
    
            createEntry(hash, key, value, bucketIndex);
        }
    
        void transfer(Entry[] newTable) {
            int newCapacity = newTable.length;
            for (Entry<K,V> e : table) {
                while(null != e) {
                    Entry<K,V> next = e.next;//第一个线程执行到停止,第二个线程执行完后,第一个线程接着执行
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];//此处理发生循环:原table为3->7 ,e=3 当第二个线程执行完后,newTable[i]=7->3   第一个线程执行此行时,  3  ->(7->3) 此时产生环
                    newTable[i] = e;
                    e = next;
                }
            }
        }
    

    并发执行时,从entry头开始拷贝时
    原:entry head 3 ->7 tail
    新:entry head 7 ->3 tail

    相关文章

      网友评论

          本文标题:HashMap实现原理

          本文链接:https://www.haomeiwen.com/subject/prsftftx.html