简读HashMap源码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QQB67G8COM/article/details/89315152

**
HashMap大致等于Hashtable
当HashMap使用在迭代计算中时,迭代时间与HashMap的容量大小成正比关系,如果迭代性能很重要就不要将初始容量(initial capacity)设置太高(或负载因子(load factor)设置太低)。
capacity是hash table中的buckets,initial capacity只是创建hash table时的容量。
load factor是衡量在哈希表的容量被自动增加之前哈希表被允许获得的满值的度量。
当哈希表中的条目数超过负载因子和当前容量的乘积时,哈希表将被重新哈希(重新构建内部数据结构),这样哈希表的buckets数大约是buckets的两倍。
默认负载因子为0.75f是为了较好的在时间与空间的成本之间提供一个较好的权衡。较高的值减少了空间的开销,但增加了查找的成本(反映在HashMap的大多数操作中,包括get和put)。
如果初始容量大于最大条目数除以负载因子,则不会发生重hash操作。
当要将足够多的的映射存储在HashMap中时,初始化足够大的容量要比自动重哈希更有效地存储映射。
线程安全:
Map m = Collections.synchronizedMap(new HashMap(…));
**
HashMap源码解剖:

public class HashMap<K,V> extends AbstractMap<K,V> implement Map<K,V>,Cloneable,Serializable {
	
	private static final long serialVersionUID = 362498820763181265L;

	static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认初始容量为16
	
	static final int MAXIMUM_CAPACITY = 1 << 30;		//最大容量为1>>30也就是2的30次方
	
	static final float DEFAULT_LOAD_FACTOR = 0.75f;		//默认加载因子为0.75,键值对的数量大于16*0.75=12时就会触发扩容
	
	static final int TREEIFY_THRESHOLD = 8;//扩容时,发现链表长度大于8有可能转换为红黑树
	
	static final int UNTREEIFY_THRESHOLD = 6;//扩容时,发现链表长度小于6就会将树转化回链表,table[i]则转换为普通桶
	
	static final int MIN_TREEIFY_CAPACITY = 64;//转换为树之前进行一次判断,如果key-value对的数量大于64才会转化为树。避免初期过多的key-value对保存在同一条链中而作不必要的转化

	//基本的hash bin node.others:TreeNode subclass and LinkedHashMap for its Entry subclass.
	static class Node<K,V> implements Map.Entry<K,V> {
		final int hash;		//hash随key而固定,都是不可修改的
		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;
		}

		//final禁止方法重写,如果在class前就禁止class被继承
		public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        //返回的hashCode等于key的hashCode的n次方。n=value的hashCode
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        //设置newValue并返回oldValue
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        /**
         * 1.判断obj是否为this(即为当前Node节点本身)
         * 2.判断obj是否属于Map.Entry的实例,过滤掉其他对象,因为Node是实现Map.Entry的,避免其它对象的传入引起异常
         */
        public final boolean equals(Object o) {
            if (o == this)	//(1)	
                return true;
            if (o instanceof Map.Entry) { 		//(2)
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
	}

	/* ---------------- 静态工具 -------------- */

	static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    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
                return c;
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType)t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                        return c;
                }
            }
        }
        return null;
    }


    /* ---------------- Fields -------------- */
    //transient的意思为短暂的、暂时的,也就是该字段修饰的fields是不参与序列化的,除此之外HashMap中的非transient的fields都会进行序列化操作。

    transient Node<K,V>[] table;//数组+链表

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

    transient int size;

    int threshold;  //阈值:capacity * load factor

    final float loadFactor;//hash table的负载因子

    /* ---------------- Public operations -------------- */
    
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    /**
     * Implements Map.put and related methods
     *
     * @param hash 对key进一步hashCode来确保散列均匀
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, 不改变存在的value
     * @param evict if false, 表处于创建模式
     * @return 返回前一个值,如果没有则为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;
        //(1)如果tab为null,则创建tab
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;	//扩容机制resize
        //(2)计算index,并对null进行处理
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; 
            K k;
            //(3)如果key存在,则直接覆盖value
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)	//判断p是否为红黑树,true 则创建树
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {								//next节点为null
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //next节点非null判断插入的hash-key-value是否与该节点的相等,相等则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;
                //onlyIfAbsent=true时不改变存在的value,默认false
                if (!onlyIfAbsent || oldValue == null)//默认true
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

    //扩容方法,返回resize后的table
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;																		//旧标签=>数组+链表,数组的每一个位置即标签
        int oldCap = (oldTab == null) ? 0 : oldTab.length;												//旧容器大小
        int oldThr = threshold;																			//旧阈值
        int newCap, newThr = 0;																			//初始化新容器大小=旧阈值=0
        if (oldCap > 0) {																				//判读table是否存在
            if (oldCap >= MAXIMUM_CAPACITY) {																//判断旧容器大小是否>=规定的最大值了(2的30次方)
                threshold = Integer.MAX_VALUE;																	//阈值设置为32为整形最大值(2<<30-1),二进制中30个0,1个1,1个符号为
                return oldTab;																					
            } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) 	//16<=(newCap=oldCap<<1)=<整形最大值,每次以2的次方进行扩充	
                newThr = oldThr << 1; 																			//double threshold
        }else if (oldThr > 0) 																			//旧阈值>=0时说明table已经存在,新容量设置为旧阈值														
            newCap = oldThr;
        else {               																			//初始阈值为0表示使用默认值,table未进行创建时阈值默认为0,开始设定容器大小和阈值
            newCap = DEFAULT_INITIAL_CAPACITY;																//初始容量为默认容量
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);									//新阈值=默认加载因子*默认容器大小 
        }
        if (newThr == 0) {																				//if新阈值==0,防异常处理
            float ft = (float)newCap * loadFactor;														
            newThr = (newCap<MAXIMUM_CAPACITY && ft<(float)MAXIMUM_CAPACITY?(int)ft:Integer.MAX_VALUE); 
        }
        threshold = newThr;																				
        @SuppressWarnings({"rawtypes","unchecked"})														//"rawtypes":传参时带泛型的参数,"unchecked":不显示警告
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];												//新建newTap数组[newCap]
        table = newTab;																					//旧标签!=null		
        if (oldTab != null) {//最为消耗性能的一部分,将oldTap转移到newTap,这也是为什么建议一开始就应该把HashMap的大小初始化好,如果中途需要插入100W条数据,你想想中间要执行这块代码体多少次?
            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;
    }
    //--------------------------------------------------------------------------------------
	//树化:将容器转化为树,传入标签以及标签的hash值
    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)//小于64不树化
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            //将tap[(n-1)&hash]链表转换为树
            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);
        }
    }
	//----------------------------------------------------------------------------------------------------------
    //获取元素与put(key,value)相对
    public V get(Object key) {
        Node<K,V> e;
        //传入hash key和key对元素进行位置查找
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

    /**
     *
     * @param hash hash(key)
     * @param key the key
     * @return node,为null则返回null
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; 	//标签
        Node<K,V> first, e; //标签的第一个node;需要获取的node
        int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
        	//总是检查first node,只有当first不是想找的node时才会想链表(或红黑树)方向进行遍历
            if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))  
                return first;
            //判断next是否为null,为null说明HashMap中不存在要找的元素
            if ((e = first.next) != null) {
            	//判断是否为红黑树,通过调用树节点的方法来获取元素并返回结果
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                //do..while循环体一直查找到链表直到找到元素或到达尾部为止
                do {
                    if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
}

more:其他博客

猜你喜欢

转载自blog.csdn.net/QQB67G8COM/article/details/89315152