HashMap源码深度解析

HashMap源码深度解析

JDK8中的HashMap与JDK7的HashMap有什么不 一样?

  • JDK8中新增了红黑树,JDK8是通过数组+链表+红黑树来实现的
  • JDK7中链表的插入是用的头插法,而JDK8中则改为了尾插法
  • JDK8中的因为使用了红黑树保证了插入和查询了效率,所以实际上JDK8中 的Hash算法实现的复杂度降低了
  • JDK8中数组扩容的条件也发了变化,只会判断是否当前元素个数是否查过了 阈值,而不再判断当前put进来的元素对应的数组下标位置是否有值。
  • JDK7中是先扩容再添加新元素,JDK8中是先添加新元素然后再扩容

HashMap中PUT方法的流程?

  • JDK7中是先扩容再添加新元素,JDK8中是先添加新元素然后再扩容
  • 通过hashcode与“与操作”计算出一个数组下标
  • 在把put进来的key,value封装为一个entry对象
  • 判断数组下标对应的位置,是不是空,如果是空则把entry直接存在该数组位置
  • 如果该下标对应的位置不为空,则需要把entry插入到链表中
  • 并且还需要判断该链表中是否存在相同的key,如果存在,则更新value
  • 如果是JDK7,则使用头插法
  • 如果是JDK8,则会遍历链表,并且在遍历链表的过程中,统计当前链表的元 素个数,如果超过8个,则先把链表转变为红黑树,并且把元素插入到红黑树中

JDK8中链表转变为红黑树的条件?

  • 链表中的元素的个数为8个或超过8个
  • 同时,还要满足当前数组的长度大于或等于64才会把链表转变为红黑树。为 什么?因为链表转变为红黑树的目的是为了解决链表过长,导致查询和插入效 率慢的问题,而如果要解决这个问题,也可以通过数组扩容,把链表缩短也可 以解决这个问题。所以在数组长度还不太长的情况,可以先通过数组扩容来解 决链表过长的问题。

HashMap扩容流程是怎样的?

  1. HashMap的扩容指的就是数组的扩容, 因为数组占用的是连续内存空间, 所以数组的扩容其实只能新开一个新的数组,然后把老数组上的元素转移到新 数组上来,这样才是数组的扩容
  2. 在HashMap中也是一样,先新建一个2被数组大小的数组
  3. 然后遍历老数组上的没一个位置,如果这个位置上是一个链表,就把这个链 表上的元素转移到新数组上去
  4. 在这个过程中就需要遍历链表,当然jdk7,和jdk8在这个实现时是有不一样 的,jdk7就是简单的遍历链表上的没一个元素,然后按每个元素的hashcode结 合新数组的长度重新计算得出一个下标,而重新得到的这个数组下标很可能和 之前的数组下标是不一样的,这样子就达到了一种效果,就是扩容之后,某个 链表会变短,这也就达到了扩容的目的,缩短链表长度,提高了查询效率
  5. 而在jdk8中,因为涉及到红黑树,这个其实比较复杂,jdk8中其实还会用到 一个双向链表来维护红黑树中的元素,所以jdk8中在转移某个位置上的元素 时,会去判断如果这个位置是一个红黑树,那么会遍历该位置的双向链表,遍 历双向链表统计哪些元素在扩容完之后还是原位置,哪些元素在扩容之后在新 位置,这样遍历完双向链表后,就会得到两个子链表,一个放在原下标位置, 一个放在新下标位置,如果原下标位置或新下标位置没有元素,则红黑树不用 拆分,否则判断这两个子链表的长度,如果超过八,则转成红黑树放到对应的 位置,否则把单向链表放到对应的位置。
  6. 元素转移完了之后,在把新数组对象赋值给HashMap的table属性,老数组 会被回收到

源码分析

 // 放入元素 
 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
 }
  // 实际的调用逻辑
  final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
             // 数组扩容或者初始化数组
            n = (tab = resize()).length; 
        if ((p = tab[i = (n - 1) & hash]) == null)
        	// 如果没有hash冲突,则直接插入数组
            tab[i] = newNode(hash, key, value, null); 
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k)))) // hash和值都相等 更新
                e = p;
            else if (p instanceof TreeNode)  // 如果是红黑树
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        // TREEIFY_THRESHOLD 默认为8 如果大于等于8 则转化为红黑树结构
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize(); // 扩容操作
        afterNodeInsertion(evict);
        return null;
    }
    

拓展

HashSet

  • 元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有 该元素的HashCode决定的,其位置其实是固定的)
  • 底层采用HashMap来实现
 public HashSet() {
        map = new HashMap<>();
    }

ConcurrentHashMap(并发安全map)

  • 存储结构

    底层采用数组、链表、红黑树 内部大量采用CAS操作。并发控制使⽤synchronized 和 CAS 来操作来实现的。

    final V putVal(K key, V value, boolean onlyIfAbsent) {
            if (key == null || value == null) throw new NullPointerException();
            int hash = spread(key.hashCode());
            int binCount = 0;
            for (Node<K,V>[] tab = table;;) {
                Node<K,V> f; int n, i, fh; K fk; V fv;
                if (tab == null || (n = tab.length) == 0)
                    tab = initTable();
                else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                   // 采用CAS操作
                    if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
                        break;                   // no lock when adding to empty bin
                }
                else if ((fh = f.hash) == MOVED)
                    tab = helpTransfer(tab, f);
                else if (onlyIfAbsent // check first node without acquiring lock
                         && fh == hash
                         && ((fk = f.key) == key || (fk != null && key.equals(fk)))
                         && (fv = f.val) != null)
                    return fv;
                else {
                    V oldVal = null;
                    // synchronized 保证操作的原子性
                    synchronized (f) {
                        if (tabAt(tab, i) == f) {
                            if (fh >= 0) {
                                binCount = 1;
                                for (Node<K,V> e = f;; ++binCount) {
                                    K ek;
                                    if (e.hash == hash &&
                                        ((ek = e.key) == key ||
                                         (ek != null && key.equals(ek)))) {
                                        oldVal = e.val;
                                        if (!onlyIfAbsent)
                                            e.val = value;
                                        break;
                                    }
                                    Node<K,V> pred = e;
                                    if ((e = e.next) == null) {
                                        pred.next = new Node<K,V>(hash, key, value);
                                        break;
                                    }
                                }
                            }
                            else if (f instanceof TreeBin) {
                                Node<K,V> p;
                                binCount = 2;
                                if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                               value)) != null) {
                                    oldVal = p.val;
                                    if (!onlyIfAbsent)
                                        p.val = value;
                                }
                            }
                            else if (f instanceof ReservationNode)
                                throw new IllegalStateException("Recursive update");
                        }
                    }
                    if (binCount != 0) {
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, i);
                        if (oldVal != null)
                            return oldVal;
                        break;
                    }
                }
            }
            addCount(1L, binCount);
            return null;
        }
    
    
    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
            Node<K,V>[] nextTab; int sc;
            if (tab != null && (f instanceof ForwardingNode) &&
                (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
                int rs = resizeStamp(tab.length);
                while (nextTab == nextTable && table == tab &&
                       (sc = sizeCtl) < 0) {
                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                        sc == rs + MAX_RESIZERS || transferIndex <= 0)
                        break;
                    // 采用CAS操作
                    if (U.compareAndSetInt(this, SIZECTL, sc, sc + 1)) {
                        transfer(tab, nextTab);
                        break;
                    }
                }
                return nextTab;
            }
            return table;
        }
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值