您的位置:首页 > 编程语言 > Java开发

jdk 1.8 hashmap源码解读(详细)(上)

2017-10-26 18:04 549 查看
package java.util;

import java.io.IOException;

import java.io.InvalidObjectException;

import java.io.Serializable;

import java.lang.reflect.ParameterizedType;

import java.lang.reflect.Type;

import java.util.function.BiConsumer;

import java.util.function.BiFunction;

import java.util.function.Consumer;

import java.util.function.Function;

    /**

     * 默认的初始化大小16

     */

    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;

    /**

     *使用树计算的阀值 8  (当通上的节点数大于8转成树)

     */

    static final int TREEIFY_THRESHOLD = 8;

    /**

     * 不使用树的阀值 6 (桶节点小于6转化成链表)

     */

    static final int UNTREEIFY_THRESHOLD = 6;

    /**

     * 当为最小的树的时候数组容量为64

     */

    static final int MIN_TREEIFY_CAPACITY = 64;

    /**

     * 内部类实现map.entry<k,v>结构,可以看做基本单元,一个节点

     */

    static class Node<K,V> implements Map.Entry<K,V> {

        final int hash; //节点属性哈希值

        final K key;

        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;

        }

        public final K getKey()        { return key; }

        public final V getValue()      { return value; }

        public final String toString() { return key + "=" + value; }

        ///节点的哈希值算法,key和value哈希取异或 ,因此节点本身的hashCode和key和value都有关系

        public final int hashCode() {

            return Objects.hashCode(key) ^ Objects.hashCode(value);

        }

        ///设置值,返回就值

        public final V setValue(V newValue) {

            V oldValue = value;

            value = newValue;

            return oldValue;

        }

        //节点是否相等,key相等,value相等

        public final boolean equals(Object o) {

            if (o == this)

                return true;

            if (o instanceof Map.Entry) {

                Map.Entry<?,?> e = (Map.Entry<?,?>)o;

                if (Objects.equals(key, e.getKey()) &&

                    Objects.equals(value, e.getValue())) ///多了个判断不为null

                    return true;

            }

            return false;

        }

    }

    /* ---------------- Static utilities -------------- */

    

    

    ///hash计算,可以看出16位有效位

    static final int hash(Object key) {

        int h;

        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); ///32位的,把高位和低位异或,使高位参与运算,分布更均匀,避免碰撞

    }

    //获取对象的Comparable<>的泛型的string.class

    static Class<?> comparableClassFor(Object x) {

        if (x instanceof Comparable) {  //判断是否实现 比较接口

            Class<?> c;

            Type[] ts, as;

            Type t;

            ParameterizedType p;

            if ((c = x.getClass()) == String.class) // bypass checks   说明:这里有两步 1.赋值c,2.判断string.class

                return c;  //如果是string类型,返回string.class

            if ((ts = c.getGenericInterfaces()) != null) {  //c对象实现的所有接口,如果是接口,泛型的原型

                for (int i = 0; i < ts.length; ++i) {

                    if (((t = ts[i]) instanceof ParameterizedType) &&//判断是否包含接口

                        ((p = (ParameterizedType)t).getRawType() ==

                         Comparable.class) &&//此接口是否是Comparable接口

                        (as = p.getActualTypeArguments()) != null &&

                        as.length == 1 && as[0] == c) // 判断第一个类型是不是String.class类型的

                        return c;

                }

            }

        }

        return null;

    }

    /**

     * x的Class文件为kc,且不为null,k>x 1,等0,小-1

     */

    @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable

    static int compareComparables(Class<?> kc, Object k, Object x) {

        return (x == null || x.getClass() != kc ? 0 :

                ((Comparable)k).compareT
10199
o(x));  //如果k>x 为1 等为0 小-1

    }

    /**

     * 返回比给定整数大且最接近的2的幂次方整数,如10,返回16

     */

    static final int tableSizeFor(int cap) {

        int n = cap - 1;

        n |= n >>> 1;

        n |= n >>> 2;

        n |= n >>> 4;

        n |= n >>> 8;

        n |= n >>> 16;

        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;

    }

    /* ---------------- Fields -------------- */

    

     ///节点的数组,长度2的次数

    transient Node<K,V>[] table;

    /**

     * 存放具体的键值对的set集合

     */

    transient Set<Map.Entry<K,V>> entrySet;

    /**

     * 实际存在的键值对的大小.

     */

    transient int size;

    /**

     * 改变的次数

     */

    transient int modCount;

    /**

     *

     *临界阀值扩容:容量*加载因子(下次改变的大小)

     *

     */

    int threshold;

    /**

     *

     * 对于数组的加载因子

     */

    final float loadFactor;

    /* ---------------- Public operations -------------- */

    /**

     * 初始化,构造函数:阀值大小   加载因子(浮点级)

     */

    public HashMap(int initialCapacity, float loadFactor) {

        if (initialCapacity < 0)///初始容量小于0 报错

            throw new IllegalArgumentException("Illegal initial capacity: " +

                                               initialCapacity);

        if (initialCapacity > MAXIMUM_CAPACITY)

            initialCapacity = MAXIMUM_CAPACITY; //大于最大取最大,2的31次方,大约21亿

        if (loadFactor <= 0 || Float.isNaN(loadFactor))////加载因子小于0,或者不是数字 报错

            throw new IllegalArgumentException("Illegal load factor: " +

                                               loadFactor);

        this.loadFactor = loadFactor;  

        this.threshold = tableSizeFor(initialCapacity); //初始化阀值的大小

    }

    /**

     *

     *

     * 构造函数:加载因子取默认0.75,大小输入

     */

    public HashMap(int initialCapacity) {

        this(initialCapacity, DEFAULT_LOAD_FACTOR);

    }

    /**

     * 构造函数:加载因子0.75

     */

    public HashMap() {

        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted

    }

    /**

     *

     * 以map<entry<,>>为初始化的构造函数

     */

    public HashMap(Map<? extends K, ? extends V> m) {

        this.loadFactor = DEFAULT_LOAD_FACTOR;

        putMapEntries(m, false);

    }

    /**

     *将map中的值加入本hashmap中

     *

     */

    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {

        int s = m.size(); ///加入map的大小

        if (s > 0) {        

            if (table == null) { // pre-size  ///刚开始的时候note[]为null

                float ft = ((float)s / loadFactor) + 1.0F;   /////////////////////////由加载因子和实际大小的值推出应该赋予的真实容量,

                int t = ((ft < (float)MAXIMUM_CAPACITY) ? //////得到值小于最大值,取得到值,大于最大,取最大    ////继上:这里说明+1.0F的作用,是为了在下一步(int)转型的时候小数

                         (int)ft : MAXIMUM_CAPACITY);       ////继上:去掉问题的解决,如果不加,就会算出值小于真实容量,反过来即阀值小于加入的map大小

                if (t > threshold) //需要容量大于设置阀值 :意味着放入map,map的值达到了扩容的阀值,即是放入的map大小比设置的map阀值大(思想:为上步的需求计算的容错)

                    threshold = tableSizeFor(t);//调大阀值,为可能出现的扩容做准备,即是初始化阀值

            }

            

            

            else if (s > threshold) ///如果放入的table不为null,且放入的map实际值大于当前的阀值    

                resize(); ///扩容,把已有的分布到新表并扩容 设置阀值

            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {   ///

                K key = e.getKey();

                V value = e.getValue();

                putVal(hash(key), key, value, false, evict);//循环放入 存在数组的首链替换,链中不替换

            }

        }

    }

    /**

     *返回大小

     *

     */

    public int size() {

        return size;

    }

    /**

     * 是否为null长度为0

     *

     */

    public boolean isEmpty() {

        return size == 0;

    }

    /**

     * 由key获得值,先获得节点,在获得值

     */

    public V get(Object key) {

        Node<K,V> e;

        return (e = getNode(hash(key), key)) == null ? null : e.value;

    }

    /**

     *

     *查找所需要的节点

     * 第一步:判断数组的首连接

     * 第二步:如果首连接符合,返回首连接,不符合,判断下个连接是否为null,

     *为null返回null,不为null,判断是否首连接是否是树连接,

     *是:查找树,不是:查找从首连接的下一个开始遍历查找

     */

    final Node<K,V> getNode(int hash, Object key) {

        Node<K,V>[] tab;

        Node<K,V> first, e;

        int n; K k;

        if ((tab = table) != null && (n = tab.length) > 0 &&

            (first = tab[(n - 1) & hash]) != null) { ////在现有数组不为null和长度大于0前提下,得到坐标的首连接不为null

            if (first.hash == hash && ///查看首连接是否是符合要求的的节点(hash值和key值)

                ((k = first.key) == key || (key != null && key.equals(k))))

                return first; //符合返回

            if ((e = first.next) != null) { ///首连接下一个不符合 包含把首连接下一个赋予e

                if (first instanceof TreeNode)  //判断首连接是否是树连接

                    return ((TreeNode<K,V>)first).getTreeNode(hash, key); //从树中hash和key获取值

                do {

                    if (e.hash == hash && //如果不是tree遍历连接的每个节点

                        ((k = e.key) == key || (key != null && key.equals(k))))

                        return e;

                } while ((e = e.next) != null);

            }

        }

        return null;

    }

    /**

     *

     *

     * 是否包含:查找,返回null,为true包含,返回值,为false,不包含

     */

    public boolean containsKey(Object key) {

        return getNode(hash(key), key) != null;

    }

    /**

     *                

     * 放入key,和value         

     */

    public V put(K key, V value) {

        return putVal(hash(key), key, value, false, true);///放入,false的时候,在onlyifabsent取fasle的时候默认数组首位置替换,支持linkedhashmap的回调

    }

    /**

     *

     * 加入值

     *嗯,代码有点小长,总结下:

     *第一步:判断hashmap数组是否为null,是:初始化。

     *第二步:找到数组下标的首节点,为null加入,

     *第三步,不为null,符合hash和key相等条件,取出当前节点,在最后判断当输入的条件为true和不为null时,不替换,其他替换,在最后设置linkedhashmap回调

     *第四步:不为null,不符合条件,判断是否是树节点,是加入

     *第五步:不为null,首节点不符合条件,不是树节点,只有是链节点,for循环查找,下节点为null,加入下节点,当加入长度达到7,转换成树,如果已经存在不加入

     *第六部:改变变量加一,判断是否达到阀值和回调

     *注意,当加入的值在数组的链的首位置的时候当输入值true和不为空的时候不进行替换,但是当为链的其他位置的时候已经存在key值value不替换,value

     *

     */                                              a.为false替换  b.为true当null才替换

    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)//当前现有的节点为null或者0 这里包含把现有数组赋予tab和数组长度赋予了n

            n = (tab = resize()).length; //旧节点为null,对其初始化 容量为16的数组

        if ((p = tab[i = (n - 1) & hash]) == null) //如果当前插入位置值为null  p=传入当前的值 (第一个note的对象)

            tab[i] = newNode(hash, key, value, null);//在当前节点创建node,

        else { //如果有值

            Node<K,V> e; K k;

            if (p.hash == hash &&  //把传入的hash和hash对比  在相等的前提下key值进行对比相等

                ((k = p.key) == key || (key != null && key.equals(k))))

                e = p;//取出当前值 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

            else if (p instanceof TreeNode)//当hash和key比较失败的时候,判断是否为红黑树

                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//8放入红黑树

            else { //链首位的hash和key不服和,也不符合树节点,只有在链的其他位置

                for (int binCount = 0; ; ++binCount) { //无限循环查找 ///注意当多线程的情况下,这里容易形成死循环,cpu爆

                    if ((e = p.next) == null) { //把首节点的下一个节点指向e,如果为null

                        p.next = newNode(hash, key, value, null); 创建新节点,并把它指向p首节点

                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st//如果本链添加的长度大于等于TREEIFY_THRESHOLD-1=7

                            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  ///在数组的首节点相等时候,对应上面的XXXXX所在行

                V oldValue = e.value;  //取出旧值

                if (!onlyIfAbsent || oldValue == null)//旧值为null或者输入条件为false时 进行新旧值替换

                    e.value = value;

                afterNodeAccess(e); ///当为linkedhashmap时候许可回调

                return oldValue;  //返回旧值

            }

        }

        ++modCount; //改变加一

        if (++size > threshold) ///实际值大小大于阀值

            resize(); 扩容

        afterNodeInsertion(evict); //当为linkedhashmap时候许可回调

        return null;返回null

    }

    /**

     *

     * 扩容机制,扩容

     *

     *嗯,有点长,总结下:

     *第一步 A:判断判断是否存在旧值,如果旧结点达最大,就让他大吧!如果没有达到最大,

     *容量扩大2倍,阀值扩大2倍。B:判断没有存在旧值,判断那一步初始化阶段,没有内容,有没阀值,

     *然后if对阀值补充,将最新阀值赋予当前阀值.

     *第二步:A.赋值并释放当前数组.B如果单节点,直接均匀放入C.如果不是单节点是树节点,

     *放入树节点D.如果不是单节点也不是树节点,把当前的链分两条分别放入对应新表两部分对应下标(下标之差为旧表的数组大小)

     */

    final Node<K,V>[] resize() {   

        Node<K,V>[] oldTab = table; //取出现有接点数组

        int oldCap = (oldTab == null) ? 0 : oldTab.length;///获取旧节点的长度  刚刚掉用构造函数的时候,没有值,大小取0

        int oldThr = threshold; //旧节点的扩容阀值

        int newCap, newThr = 0; //新节点的容量,阀值=0

        if (oldCap > 0) {  //存在旧节点

            if (oldCap >= MAXIMUM_CAPACITY) { //旧节点达到最大值

                threshold = Integer.MAX_VALUE;   //阀值取最大

                return oldTab; //返回旧节点数组

            }

            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&/// 新节点取旧节点的的二倍小于最大值

                     oldCap >= DEFAULT_INITIAL_CAPACITY)////旧结点大于默认的初始容量

                newThr = oldThr << 1; // double threshold  //新的阀值取旧的2倍

        }    ///不存在旧结点

        else if (oldThr > 0) // 初始化,没有内容,但是设置阀值的情况 hashmap(inintcap,factoy)和hashmap(initcap)情况

            newCap = oldThr; 新的阀值取旧阀值

        else {               // 初始化,没有内容,也没有阀值的情况 hashmap()和hashmap(map)准备

            newCap = DEFAULT_INITIAL_CAPACITY; //默认容量16

            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); //默认阀值16*0.75=12

        }

      ///新阀值为0    (什么时候出现?在上面的初始化,没有内容却又阀值)   

       if (newThr == 0) {

            float ft = (float)newCap * loadFactor; //在上面newCap是旧设置阀值*加载因子 扩容变小了??????  >_<

            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?

                      (int)ft : Integer.MAX_VALUE); //

        }

        threshold = newThr;//总上所述,最后新阀值赋予阀值

        @SuppressWarnings({"rawtypes","unchecked"})

            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];////新的节点数组

        table = newTab;///新接节点数组加入结构中

        if (oldTab != null) {//扩容前的旧有值

            for (int j = 0; j < oldCap; ++j) {

                Node<K,V> e; //新节点

                if ((e = oldTab[j]) != null) {

                    oldTab[j] = null;//释放旧节点数组中的值,便于GC回收,把值赋予e

                    if (e.next == null)//如果当前就节点下个节点为空,即单节点

                        newTab[e.hash & (newCap - 1)] = e;///把e放入新数组中,下标以e的hash和容量-1与为下标

                        

                        ///https://www.zhihu.com/question/44460053  ///相当于对值的取膜

                        ///http://blog.csdn.net/zccracker/article/details/54378852 《--大力推荐文章   

                    else if (e instanceof TreeNode)//如果当前下个不为null,并为树节点,

                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); ////额......>_< 把当前的节点 在新表中以j分成散列度的oldCap大小数,

                    else { // preserve order //下个节点不为null,不是树节点

                        Node<K,V> loHead = null, loTail = null;

                        Node<K,V> hiHead = null, hiTail = null;

                        Node<K,V> next;

                        do {

                            next = e.next;

                            ///e的hash和就oldCap是否为0与分割成两张链表。理解:oldCap的为16时10000,在以后每次2倍相当于向前移一位,都是100xxxx格式,取&是否为0的概率就变成对半分了

                            if ((e.hash & oldCap) == 0) {  

                                if (loTail == null) //当前为null,给当前,否则给下一个

                                    loHead = e;//

                                else

                                    loTail.next = e;

                                loTail = e;

                            }

                            else {

                                if (hiTail == null)//当前为null,给当前,否则给下一个

                                    hiHead = e;

                                else

                                    hiTail.next = e;

                                hiTail = e;

                            }

                        } while ((e = next) != null);//知道e的下一个为null为止,单向链表结构停止

                        if (loTail != null) {//当前节点不为null

                            loTail.next = null;//下个为null,(意味着放完了)

                            newTab[j] = loHead; //把当前链放入数组中

                        }

                        if (hiTail != null) {//当前节点不为null

                            hiTail.next = null;下个为null,//(意味着放完了)

                            newTab[j + oldCap] = hiHead;//把当前链放入数组中     这里把新的链放入之中,正好对应新的表大小为原来的两倍

                        }

                    }

                }

            }

        }

        return newTab;

    }

    /**

     * 当到链的长度达到7的时候变成树

     */

    final void treeifyBin(Node<K,V>[] tab, int hash) {

        int n, index; Node<K,V> e;

        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)///如果为数组为null,或者数组的长度小于最小树容量64,扩容

            resize();

        else if ((e = tab[index = (n - 1) & hash]) != null) { ///如果数组的长度达到就转化成节点树结构

            TreeNode<K,V> hd = null, tl = null;

            do {

                TreeNode<K,V> p = replacementTreeNode(e, null); ///替换成树节点,从第一个开始

                if (tl == null)

                    hd = p;

                else {

                    p.prev = tl;

                    tl.next = p;

                }

                tl = p;

            } while ((e = e.next) != null);

            if ((tab[index] = hd) != null)

                hd.treeify(tab);

        }

    }

    /**

     * 将map放入hashmap中 许可启用linkedhashmap

     *

     */

    public void putAll(Map<? extends K, ? extends V> m) {

        putMapEntries(m, true);

    }

    /**

     * 由key来删除节点值,

     */

    public V remove(Object key) {

        Node<K,V> e;

        return (e = removeNode(hash(key), key, null, false, true)) == null ?  //含有操作,调用元素,取出节点,得到节点的值

            null : e.value;

    }

    /**

     *移除节点,有点多,总结下

     *第一步:判断当前hash含有链,然后判断首节点,首节点不符合,

     *判断下个节点是不是树,是树节点转到树,由树去取出节点,不是树,while检查链组

     *第二步:得到要删除的节点,判断输入匹配条件,

     *然后判断是否树,是由树去删除,不是,判断是否为首节点,是将要删的节点下个接点方到数组中

     *不是首节点,将上接点的下一个指向下节点的上一个,返回删除节点

     */

    final Node<K,V> removeNode(int hash, Object key, Object value, ///存在,只要值相等一定删

                               boolean matchValue, boolean movable) {

                               //如果false 不管等不等都删   // 树节点的移除标记         

       Node<K,V>[] tab; Node<K,V> p; int n, index;

        if ((tab = table) != null && (n = tab.length) > 0 &&

            (p = tab[index = (n - 1) & hash]) != null) { //当前数组不为null,长度大于0,找到位置存在元素

            Node<K,V> node = null, e; K k; V v;

            if (p.hash == hash &&

                ((k = p.key) == key || (key != null && key.equals(k)))) //检查第一个节点,赋予node

                node = p;

            else if ((e = p.next) != null) {///第一节点不符合,检查下一节点

                if (p instanceof TreeNode)

                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);//是否为树,获取

                else {

                    do {  ///第一节点不符合,第二节点不是树节点,只有链表结构了

                        if (e.hash == hash &&

                            ((k = e.key) == key ||    ///第二节点是否符合

                             (key != null && key.equals(k)))) {

                            node = e;

                            break;   

                        }

                        p = e;   ///标记:BBBBBBBBBB

                    } while ((e = e.next) != null);

                }

            }

            ///在这一步把获取到的节点在node上

            if (node != null && (!matchValue || (v = node.value) == value || ///在删除的节点不为null的前提下,当

                                 (value != null && value.equals(v)))) {

                if (node instanceof TreeNode)

                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); //如果为树,移除树节点

                else if (node == p)   /////是否是首节点

                    tab[index] = node.next;  ///把要删的节点的下个节点定义为首节点

                else

                    p.next = node.next; //是一般节点 把要删除的节点的上个节点指向要删节点的下一节点 注意:要和上面查找删除节点的判断结合一起看,BBBBBBB行

                ++modCount; //修改加一

                --size; //大小减一

                afterNodeRemoval(node); //linkedhashmap的回调

                return node; 返回要删节点

            }

        } //没有找到删除节点,(数组所在index链为null)

        return null;

    }

    /**

     * 清除,存在值,把size为0,把数组每个变成null有GC回收

     */

    public void clear() {

        Node<K,V>[] tab;

        modCount++;

        if ((tab = table) != null && size > 0) {

            size = 0;

            for (int i = 0; i < tab.length; ++i)

                tab[i] = null;

        }

    }

    /**

     *

     * 遍历节点比较value是否包含

     */

    public boolean containsValue(Object value) {

        Node<K,V>[] tab; V v;

        if ((tab = table) != null && size > 0) {

            for (int i = 0; i < tab.length; ++i) {

                for (Node<K,V> e = tab[i]; e != null; e = e.next) {

                    if ((v = e.value) == value ||

                        (value != null && value.equals(v)))

                        return true;

                }

            }

        }

        return false;

    }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: