HashMap源码(JDK1.8)-手动注释

互联网 2021/4/8 12:13:14

转: HashMap源码(JDK1.8)-手动注释 HashMap简介 HashMap是一种K-V映射的一种数据结构,通过K(key)值能实现在O(1)的时间复杂度下找到对应的V(value)。JDK1.8之前,HashMap的底层数据结构是数组+链表,数组中的每个元素称为一个Entry,包含(hash,key,value,next)这…

转:

HashMap源码(JDK1.8)-手动注释

HashMap简介

HashMap是一种K-V映射的一种数据结构,通过K(key)值能实现在O(1)的时间复杂度下找到对应的V(value)。JDK1.8之前,HashMap的底层数据结构是数组+链表,数组中的每个元素称为一个Entry,包含(hash,key,value,next)这四个元素,其中链表是用来解决碰撞(冲突)的,如果hash值相同即对应于数组中同一一个下标,此时会利用链表将元素插入链表的尾部,(JDK1.8是头插法)。在JDK1.8及之后,底层的数据结构是:数组+(链表,红黑树),引入红黑树是为了避免链表过长,影响元素值的查找,因此当整体的数组大小大于64时,并且链表的长度大于或等于8时,会把链表转化成红黑树。在HashMap这一数据结构中,常见的方法有get、put等,在查找或者插入元素时都会建立临时数组和指针。常见的Map有:HashMap、TreeMap、LinkedHashMap、HashTable、concurrentHashMap等。掌握HashMap对于面试时很有帮助的,这是面试常问的知识点。

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;
    } 
    //getKey
    public final K getKey()  {return key;}
    public final V getValue() {return value;}
    public final String toString() {return 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;
    }
    
    
    //需要重写equals,当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()))
                return true;
        }
        return false;
    }
    
    //hash是key值的hashcode高低16位异或,从这里可以知道jdk1.8hashmap的key是可以为null
    //若为null,取0,hashtable中的key是不能为null的
    static final int hash(Object key){
        int h;
        return (key == null)?0:(h = key.hashCode()^(h>>>16));
    }
    
    //给出一个初始容量,会根据给定的初始容量,给出最近的2的多少平方
    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 >= MAXIMU_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
    
    //同上,另一种实现方法,得到最近的2的多少的平方
    static final int tableSizeFor(int cap){
        int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
        return (n < 0) ? 1 : (n >= MAXIMU_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
    
    //其中numberOfLeadingZeros方法,采用了从大到小进行判断
    public static int numberOfLeadingZeros(int i){
        if(i <= 0){
            return i == 0 ? 32 : 0;
        }
        int n = 31;
        if(i >= 1 << 16) {n -= 16; i >>>= 16;}
        if(i >= 1 << 8) {n -= 8; i >>>= 8;}
        if(i >= 1 << 4) {n -= 4; i >>>= 4;}
        if(i >= 1 << 2) {n -= 2; i>>>= 2;}
        return n - (i >>> 1);
    }
    
    //初始化HashMap
    public HashMap(int initialCapacity, float loadFactor){
        if(initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity:" + initialCapacity);
        if(initialCapacity > MAXIMUM_CAPACITY){
            initalCapacity = MAXIMUM_CAPACITY;
        }
        if(loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
        this.loadFactor = loadFactor;
        this.threshlod = tableSizeFor(initialCapacity);
    }
    //获取特定key的value值
    public V get(Object key){
        Node<k,v> e;
        return (e = getNode(hash(key),key)) == null ? null : e.value;
    }
    //通过hash、key获取Node
    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){
            if(first.hash == hash && //总是先检查第一个结点
              ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
        }
        //如果不是第一个结点,则判断是否有下一个结点,接着需要判断是链表形式的还是红黑树型的
        if((e = first.next) != null){
            if(first instanceof TreeNode)
                return ((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);//进行循环,一直比对hash、key是否相等
        }
        return null;
    }
    //利用getNode进行判断
    public boolean containsKey(Object key){return getNode(hash(key),key) != null;}
    
    //put--(key,value)
    public V put(K key, V value){
        return putVal(hash(key),key,value,false,true);
    }
    //重点来看看putVal
    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)//如果此时table数据为空,进行扩容
            n = (tab = resize()).length;
        if((p = tab[i = (n - 1)&hash]) == null)  //若找到下标,此时没有值,即为null,则创建结点
            tab[i] = newNode(hash,key,value,null);
        else{//否则将将进行遍历链表
            Node<k,v> e; K k;
            //先检查头结点,如果hash,key相等,则已经插入了该结点
            if(p.hash == hash &&
              ((k = e.key) == key) || (key != null && key.equals(k)))
                e = p;
            //判断插入的结点p是否是树结点
            else if(p isinstanceof 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);
                        //如果binCount>=7时,链表树化为红黑树
                        if(binCount >= TREEIFY_THRESHOLD - 1)
                            treeifyBin(tab,hash);
                        break;
                    }//找到相等的结点,直接break
                    if(e.hash == hash && ((k = e.key) == key ||(key != null && key.equals(k))))
                        break;
                    //移动链表指针,指向下一个结点
                    p = e;
                }
            }
            if(e != null){//存在映射关系,但是value为空
                V oldValue = e.value;
                if(!onlyIfAbsent || oldValue == value)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if(++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    
    final Node<k,v>[] resize(){
        //定义oldTab,oldThr
        Node<k,v>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0:oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;//新的容量、新的阈值
        //进行判断
        if(oldCap > 0){
            //直接把阈值设置为最大值,返回原来的数组
            if(oldCap >= MAXIMUM_CAPACITY){
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }//容量放大两倍,阈值也放大两倍
            else if((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                   oldCap >= DEFALUT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // 阈值放大两倍
        }
        //此时,oldCap等于零,但是阈值oldThr大于零,直接用oldThr进行替换
        else if(oldThr > 0)
            newCap = oldThr;//用thre替换初始容量
        else {//此时,oldCap和oldThr都为零,进行初始值替换,表明第一次扩容
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACOR * DEFAULT_INITIAL)
        }
        //初始化阈值newThr,初始化threshold
        if(newThr == 0){
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY)?(int)ft : Integer.MAX_VALUE;
        }
        threshold = newThr;
        //建立Node型数组,对table进行赋值
        Node<k,v>[] newTab = (Node<k,v>[])new Node[newCap];
        table = newTab;
        //将oldTab中元素赋值给newTab
        if(oldTab != null){
            for(int j = 0;j < oldCap; ++j){
                Node<k,v> e;
                if((e = oldTab[j]) != null){
                    oldTab[j] = null;//释放旧的数组中的内存
                    if(e.next == null) //判断原来数组位置是否只有一个节点,则进行赋值
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)//判断是树节点
                        ((TreeNode<k,v>)e).split(this,newTab, j,oldCap);
                    else{ //rehash,高位等于索引+oldCap,即:j + oldCap;
                        Node<k,v> loHead = null, loTail = null;
                        Node<k,v> hiHead = null, hiTail = null;
                        Node<k,v> next;
                        do{
                            next = e.next;
                            if((e.hash & oldCap) == 0){ //表明是原来的位置,看这个地方是否有头结点,若无直接赋值,否则从尾部插入
                                if(loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }//表明需要从新hash到新的位置,同理如上
                            else{
                                if(hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        }while((e = next) != null);
                        if(loTail != null){ //先建立一个链表,之后将这个链表接到j索引处
                            loTail.next = null;
                            newTab[j] = loHead;
                        } // 同理只是更改了索引位置:j + hiHead
                        if(hiTail != null){
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
    
    public V remove(Object key){
        Node<k,v> e;
        return (e = removeNode(hash(key),key,null,false,true)) == null ? null : e.value;
    }
    final Node<k,v> removeNode(int hash, Object key,Object value,
                              boolean matchValue,boolean movable){
        //定义临时变量
        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){
            Node<k,v> node = null, e; K k; V v;
            //若是头结点
            if(p.hash == hash && 
               ((k = p.key) == key || (key != null && key.equals(k))))
                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;
                    } while((e = e.next) != null);
                }
            }
            if(node != null && (!matchValue || (v = node.value) == value ||
                               (value != null && value.equals(v)))){
                if(node instanceof TreeNode)
                    ((TreeNode<k,v>)node).removeTreeNode(this,tab,movale);
                //若判断是头结点,直接去掉头结点,接在后面
                else if(node == p)
                    tab[index] = node.next;
                else
                    p.next = node.next;//在链表中间,跳过该节点
                ++modCount;
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }
}
随时随地学软件编程-关注百度小程序和微信小程序
关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[HashMap源码(JDK1.8)-手动注释]http://www.zyiz.net/tech/detail-153791.html

上一篇:结对编程总章 - 结束也是新的开始

下一篇:u3d:关于程序里面雾效的动态实现

赞(0)

共有 条评论 网友评论

验证码: 看不清楚?
    关注微信小程序
    程序员编程王-随时随地学编程

    扫描二维码或查找【程序员编程王】

    可以随时随地学编程啦!

    技术文章导航 更多>
    扫一扫关注最新编程教程