2018-04-07本文参考 https://tech.meituan.com/java-hashmap.html

HashMap是大家工作比较常用的集合类,也是JAVA面试官比较青睐的面试题,本文从以下几方面深入学习JAVA HashMap

1.分析HashMap基本结构

2.HashMap put,get源码分析

3.HashMap Resize会导致线程不安全

4.小结

一 .HashMap是Key-Value 键值对存储集合,由Node类型的数组构成集合

node1 node2 null node3 node4 .. .. ..

底层数据结构用数组+链表+红黑树构成 

 /**
     * 默认数组长度大小,必须是2的幂次方
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * 
     *最大数组长度
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 默认负载因子
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     *转换红黑树的的阀值
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 数组长度扩展阀值 (capacity * load factor).
     */
    int threshold;
PS.穿插一个小知识点,java位运算
1<<4 表 1的二进制左移4位 0001 左移4位 1 0000

Node 结构是一个链表
static class Node<K,V> implements Map.Entry<K,V> {
        //Hash 值
        final int hash;
        //键值KEY
        final K key;
        //键值Value
        V value;
        //链接的下一个值
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
        ......
}

 

二.HashMap put方法分析

 

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        HashMapNB.Node<K,V>[] tab; HashMapNB.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)
            tab[i] = newNode(hash, key, value, null);//如果p为空,创建一个节点
        else {
            Node<K,V> e; K k;
            //判断key是否存在
            if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode) //以树结构形式存储
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //key不存在,遍历P-node链表
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        //链表中不存在同一个key,创建一个新node挂在链头 。因为设计者认为后put进去的会高频取
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // 如果p链表长度大于阀值8,转换红黑树存储
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;//链表中存在同一个key
                }
            }
            if (e != null) { // existing mapping for key 如果存在相同hash值和key,覆盖Node
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;//记录修改次数
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

 三 HashMap get方法分析

 final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; HashMapNB.Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
                (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node,判断头节点key是否存在
                    ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((HashMapNB.TreeNode<K,V>)first).getTreeNode(hash, key);
                //遍历链表
                do {
                    if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

 四 hashCode resize分析,引发线程安全问题

resieze 扩容capacity 和 threshold 都>>左移1位

多线程同时resize,遍历新数组对象可能存在死锁

 

posted on 2018-04-07 19:03  夜雨星辰-  阅读(127)  评论(0编辑  收藏  举报