zoukankan      html  css  js  c++  java
  • HashMap源码分析(二)--HashMap

    话不多说直接上开始

        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始容量为16
    
        
        static final int MAXIMUM_CAPACITY = 1 << 30;         
    
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
        /**
         * The bin count threshold for using a tree rather than list for a
         * bin.  Bins are converted to trees when adding an element to a
         * bin with at least this many nodes. The value must be greater
         * than 2 and should be at least 8 to mesh with assumptions in
         * tree removal about conversion back to plain bins upon
         * shrinkage.
         */
        static final int TREEIFY_THRESHOLD = 8;
    
        /**
         * The bin count threshold for untreeifying a (split) bin during a
         * resize operation. Should be less than TREEIFY_THRESHOLD, and at
         * most 6 to mesh with shrinkage detection under removal.
         */
        static final int UNTREEIFY_THRESHOLD = 6;
    
        /**
         * The smallest table capacity for which bins may be treeified.
         * (Otherwise the table is resized if too many nodes in a bin.)
         * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
         * between resizing and treeification thresholds.
         */
        static final int MIN_TREEIFY_CAPACITY = 64;

    目前知道的是,初始容量为16,最大容量为32位。

    二、分析:

    1.hashmap采用的是一个hashset的数组,以及在每个数组对应一个单向链表。

    2.每个Enrty<key,value>以节点的方式存储在链表中。

    三、具体分析:

    1.节点(Node):

        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; }
    
            public final int hashCode() {
                return Objects.hashCode(key) ^ Objects.hashCode(value);
            }
    
            public final V setValue(V newValue) {
                V oldValue = value;
                value = newValue;
                return oldValue;
            }
    
            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;
            }
        }

    (1).key值固定,因为当key发生变化是,他的存储位置必然发生变化这样的话等于删除再重建,所以hash不允许修改key值(hash值依赖于key值),当然key值固定,hash值也就固定,所以为final。

    (2).接入Map的内部接口Entry<key,value>,并且实现了其中的常用的键值对操作方法。

    (3).next节点,由于HashMap中是单向列表不同于LinkedList的双向列表,仅仅有存放了下一个节点的地址空间,一边操作。

    (4).初始化时必须要(hash,key,value,nextnode(不过当最后节点时,为null))

    2.构造方法

        public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
            this.loadFactor = loadFactor;
            this.threshold = tableSizeFor(initialCapacity);
        }
    
        /**
         * Constructs an empty <tt>HashMap</tt> with the specified initial
         * capacity and the default load factor (0.75).
         *
         * @param  initialCapacity the initial capacity.
         * @throws IllegalArgumentException if the initial capacity is negative.
         */
        public HashMap(int initialCapacity) {
            this(initialCapacity, DEFAULT_LOAD_FACTOR);
        }
    
        /**
         * Constructs an empty <tt>HashMap</tt> with the default initial capacity
         * (16) and the default load factor (0.75).
         */
        public HashMap() {
            this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
        }
      
    public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
    }

    (1).有上面源码可知,HashMap创建时必要的是初始长度(但是已经初始化),负荷系数(load Factor,用来显示size/capacity,满的程度)(也被初始化)

       threshord=capacity*loadFactor

    2.存放其他Map

        final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
            int s = m.size();
            if (s > 0) {
                if (table == null) { // pre-size
                    float ft = ((float)s / loadFactor) + 1.0F;
                    int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                             (int)ft : MAXIMUM_CAPACITY);
                    if (t > threshold)
                        threshold = tableSizeFor(t);
                }
                else if (s > threshold)
                    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);
                }
            }
        }

    (1).hashMap的容量的阈值判断,前半部分都是,for循环则是将map存储。五个参数前三个与Node相同,用于node的创建

    3.找到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 &&    //table必须不能为null,且长度大与0,且第一个值不为null
      (first = tab[(n - 1) & hash]) != null) { //这个位运算的确不明白怎么找到的,应该与hash算法相关 if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) //若key值相同且hash值相同(关于或者判断前后,目的在于null值判断) 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); } } return null; }

    (1)table用于接收通过hash值找到的链表,first则是第一个元素,e用于接收查找的元素。(标记在上方代码中)

    (2)关于TreeNode,1.8后Java设定HashMap链表的最大程度为8,如果超出这个长度则会将链表转化为属性结构,为了提高一定效率。

    4.put方法

        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))))          //判断 是否与首节点重复,key重复则覆盖,value覆盖
                    e = p;
                else if (p instanceof TreeNode)                                        //判断树节点,暂时不管
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);             
                else {                                        
                    for (int binCount = 0; ; ++binCount) {                                 //循环遍历链表,直到找到尾节点或者找到相同的key值覆盖
                        if ((e = p.next) == null) {                                //尾节点
                            p.next = newNode(hash, key, value, null);
                            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                  //覆盖,onlyIfAbsent可以选择是否覆盖旧的节点。
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);              //插入新的node
            return null;   
        }

    (1)主要注解写在上方代码后

    (2)在hashmap方法中,最为难以理解的是Node转向TreeNode,在所有添加节点的方法中都会进行判断链表下的节点数目,所以在正常看事过滤这一部分代码即可。

    5.

        final Node<K,V>[] resize() {                                           
            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 >= DEFAULT_INITIAL_CAPACITY)
                    newThr = oldThr << 1; // double threshold
            }
            else if (oldThr > 0) // initial capacity was placed in threshold
                newCap = oldThr;
            else {               // zero initial threshold signifies using defaults
                newCap = DEFAULT_INITIAL_CAPACITY;
                newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
            }
            if (newThr == 0) {
                float ft = (float)newCap * loadFactor;
                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;
                        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 { // preserve order
                            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;
                                }
                                else {
                                    if (hiTail == null)
                                        hiHead = e;
                                    else
                                        hiTail.next = e;
                                    hiTail = e;
                                }
                            } while ((e = next) != null);
                            if (loTail != null) {
                                loTail.next = null;
                                newTab[j] = loHead;
                            }
                            if (hiTail != null) {
                                hiTail.next = null;
                                newTab[j + oldCap] = hiHead;
                            }
                        }
                    }
                }
            }
            return newTab;
       }
  • 相关阅读:
    tuple 元组及字典dict
    day 49 css属性补充浮动 属性定位 抽屉作业
    day48 选择器(基本、层级 、属性) css属性
    day47 列表 表单 css初识
    day 46 http和html
    day 45索引
    day 44 练习题讲解 多表查询
    day 40 多表查询 子查询
    day39 表之间的关联关系、 补充 表操作总结 where 、group by、
    day38 数据类型 约束条件
  • 原文地址:https://www.cnblogs.com/qqwhsj/p/10859267.html
Copyright © 2011-2022 走看看