HashMap源码分析

本文深入解析了HashMap的内部实现机制,包括数据结构、主要成员变量、构造方法、扩容逻辑、put和get方法的工作原理,以及红黑树和链表转换的条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本片文章不涉及到HashMap的TreeNode的相关代码。
先看到部分成员变量,常量

	//存放数据的数组
	transient Node<K,V>[] table;
	//entry的set集合
	transient Set<Map.Entry<K,V>> entrySet;
	//HashMap中数据的个数
	transient int size;
	//HashMap中数据被修改次数,比如插入删除数据
	transient int modCount;
	//扩容阈值
    int threshold;
    //扩容因子
    final float loadFactor;
	//默认的初始容量
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
	//table的最大长度
	static final int MAXIMUM_CAPACITY = 1 << 30;
	//默认的扩容因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //链表长度大于等于该值时,升级为红黑树
    static final int TREEIFY_THRESHOLD = 8;
    //红黑树的节点小于等于6时,退化为链表
    static final int UNTREEIFY_THRESHOLD = 6;
    //table长度小于该值时,即使某个table[i]的链表的长度大于8时,也不会升级为红黑树,而是去扩容
    static final int MIN_TREEIFY_CAPACITY = 64;

看到构造方法

	//可以指定容器HashMap的初始容量 和 扩容因子
	//并不会初始化table
    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;
        //HashMap的容量必须为2的幂次方 , 使用initialCapacity计算出最接近的2的幂次方的值,作为扩容阈值
        this.threshold = tableSizeFor(initialCapacity);
    }
    public HashMap(int initialCapacity) {
    	//使用DEFAULT_LOAD_FACTOR作为扩容因子
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    public HashMap() {
    	//使用DEFAULT_LOAD_FACTOR作为扩容因子 , threshold 并没有赋值
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        //将m进行遍历,赋值到新的map
        putMapEntries(m, false);
    }
	
	//有这种需求的可以直接调用HashMap提供的该方法
	static final int tableSizeFor(int cap) {
		//需要减一,如果该值本身为2的幂次方,则不需要扩大2倍
        int n = cap - 1; 
        //连续做位移操作和或运算
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        //计算出的值即使超过了MAXIMUM_CAPACITY(最大长度),也只会返回MAXIMUM_CAPACITY
        //计算出的值小于0,则返回1
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

看到put方法

    public V put(K key, V value) {
    	//看到putValue方法
        return putVal(hash(key), key, value, false, true);
    }
    //计算key的hash值
    static final int hash(Object key) {
        int h;
      	//key为null,则hash值为0
      	//调用key的hashCode方法,赋值给h
      	//返回  h ^ (h >>> 16)    
      	//由于table的长度一般并不会很大, 当使用h &(table.length-1)计算该数据在table上的位置时,显然h高位的值 & 0全部为0,导致低位的值相同的而高位的值不同的key,落到了table的一个位置上。
      	//为了避免这种情况,需要让高位也参与到计算table位置,所以让高16位和低16位做了一次亦或运算来改变低位的值,高位的值则亦或上了16个0,等于没有发生变化。
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
    //onlyIfAbsent如果为true,则key相同的情况下,不会替换老的value,如果为false,则用新的value替换老的value
    //evict传入给afterNodeInsertion方法,HashMap的该方法为空实现。之后的文章讲到LinkedHashMap(重写了父类的afterNodeInsertion方法)的时候,再来看该方法。
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        
        //如果table为空,则初始化table,n为table的长度
        if ((tab = table) == null || (n = tab.length) == 0)
        	//初始化table
            n = (tab = resize()).length;
            
        //使用 (table的长度减一) table的长度为2的幂次方 , 减去一  则变为   0...01...1  这种数据
        //hash值 & (n-1) 则结果  必定在 0 到 长度减一 的范围 ,将该值赋值给i
        //获取table的i位置赋值为p 如果p为空
        if ((p = tab[i = (n - 1) & hash]) == null)
        	//将key,value包装为node,放入table[i]
            tab[i] = newNode(hash, key, value, null);
        //table的i位置已经有数据了
        else {
            Node<K,V> e; K k;
            //如果p的hash和put进来的key的hash相同 且 key相同,则将p赋值给e
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //如果该node为TreeNode , 已经从链表升级成为了红黑树 ,将该值添加到树中
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);    
            else {
                for (int binCount = 0; ; ++binCount) {
                	//获取p的下一个节点,赋值给e
                	//如果e为空
                    if ((e = p.next) == null) {
                    	//将key,value包装为node,将p的next设置为此node
                        p.next = newNode(hash, key, value, null);
                        //如果binCount 大于等于7 , 说明链表的长度大于等于8
                        // binCount从0开始 , 所以这里 8 -1 = 7
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        	//将链表转化为红黑树,如果table的长度小于64,会调用resize扩容,不会转化为树
                            treeifyBin(tab, hash);
                        break;
                    }
                    //如果e的key和put进来的key相同 ,则跳出循环
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    //e赋值给p,接着获取后续节点
                    p = e;
                }
            }
            //e不为空,说明存在相同的key
            if (e != null) { // existing mapping for key
            	//获取e的值
                V oldValue = e.value;
                //onlyIfAbsent 为 false ,  或者oldValue 为空 则将e的value改为put进来的value
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                //空方法交由子类实现
                afterNodeAccess(e);
                //返回老的value
                return oldValue;
            }
        }
        //modCount加1
        ++modCount;
        //size加一
        //如果size大于扩容阈值
        if (++size > threshold)
        	//table扩容
            resize();
        //空方法交由子类实现
        afterNodeInsertion(evict);
        return null;
    }    

看到resize方法,初始化table或者扩容table

    final Node<K,V>[] resize() {
    	//获取table
        Node<K,V>[] oldTab = table;
        //获取table的长度
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        //获取扩容阈值
        int oldThr = threshold;
        //新的容量,新的扩容阈值
        int newCap, newThr = 0;
        //如果oldCap大于0,说明table已经初始化过了,则进行扩容
        if (oldCap > 0) {
        	//oldCap大于等于最大长度
            if (oldCap >= MAXIMUM_CAPACITY) {
            	//扩容因子设置为Integer.MAX_VALUE
                threshold = Integer.MAX_VALUE;
                //已经最大了,没法扩容了,直接返回原本的table
                return oldTab;
            }
            //newCap 设置为 oldCap的两倍
            //如果newCap 小于最大长度 且  oldCap 大于等于 DEFAULT_INITIAL_CAPACITY(16)
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                //newThr 设置为oldThr的两倍
                newThr = oldThr << 1; // double threshold
        }
        //如果oldThr 大于 0 ,指定了容量的HashMap的构造方法oldThr 大于 0 
        else if (oldThr > 0) 
        	//newCap赋值为扩容阈值
            newCap = oldThr;
        //HashMap无参的构造方法 oldThr会为0 
        else {             
        	//newCap 为 16
            newCap = DEFAULT_INITIAL_CAPACITY;
            //newThr 为 16 * 0.75 = 12
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        //如果newThr为0,
        if (newThr == 0) {
        	//使用newCap * 扩容因子 计算 扩容阈值
            float ft = (float)newCap * loadFactor;
            //newCap 小于最大长度 且  扩容阈值 小于 最大长度 ,则扩容阈值为 (int)ft  , 否则为Integer.MAX_VALUE
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        //赋值计算后扩容阈值给threshold 变量
        threshold = newThr;
        
        //实例化新的table
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        //oldTab 不为空 ,则进行扩容操作, 否则当前只是初始化table, 直接返回
        if (oldTab != null) {
        	//遍历老的table
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                //如果oldTab[j]不为空,则要将数据迁移到newTab中
                //oldTab[j]赋值给e
                if ((e = oldTab[j]) != null) {
                	//置空原本的oldTab[j]
                    oldTab[j] = null;
                    //如果该位置只有一个数据,则将该数据的hash & newCap 计算出其在newTab上的位置,然后将newTable上对应位置的数据设置为e
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    //如果已经是一个红黑树,则迁移红黑树到newTable,如果迁移后的位置的数据量小于6,退化为链表
                    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
                            next = e.next;
                            //newCap为oldCap的2倍,则oldCap 0...10000 ->>> 0...01111   newCap 0...100000 ->>> 0...011111  
                            //使用e的hash值& oldCap(此时并没有减一,该值为2的幂次方)
                            //则此时只有一个1(0...10000),则结果值只会有两种情况,key的hash值对应的位置上的值是否为1(由于落在了一个table的位置上,说明之前的低位 & (oldCap-1) 的值都是一样的,现在用前面高的一位 与运算 这个1,来区分原来的数据),其他位置的值 & 0 全部为0了)  
                            
                            //loHead直接可以放入newTab[j] 
                            //hiHead直接可以放入newTab[j + oldCap] 
                            
                            //hash对应的位置上的值为0		
                            if ((e.hash & oldCap) == 0) {
                            	//如果还没有值,e赋值给loHead 
                                if (loTail == null)
                                    loHead = e;
                                //已经有值了,loTail下一个元素指向e
                                else
                                    loTail.next = e;
                                //改变loTail为下一个元素
                                loTail = e;
                            }
                             //hash对应的位置上的值为1
                            else {
                               //如果还没有值,e赋值给hiHead 
                                if (hiTail == null)
                                    hiHead = e;
                                //已经有值了,hiTail下一个元素指向e
                                else
                                    hiTail.next = e;
                                //改变hiTail为下一个元素
                                hiTail = e;
                            }
                        } while ((e = next) != null);  //循环e的链表直至为空
                        //loTail 不为空
                        if (loTail != null) {
                        	//loTail的next 置空  ,(这里的next 可能是 原本在oldTab的值高位为1的)
                            loTail.next = null;
                            //loHead赋值给newTab[j] ,
                            newTab[j] = loHead;
                        }
                        //hiTail 不为空, hiHead赋值给newTab[j+oldCap]  
                        if (hiTail != null) {
                        	//hiTail的next 置空  ,(这里的next 可能是 原本在oldTab的值高位为0的)
                            hiTail.next = null;
                            //hiHead赋值给newTab[j + oldCap]
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

再来看putMapEntries方法

	//putAll也调用了putMapEntries方法
    public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    	//获取m中数据个数
        int s = m.size();
        if (s > 0) {
        	//如果table 为空 , 需要初始化table
            if (table == null) { // pre-size
				//使用s 粗略计算 m的下一次的扩容阈值   +1为了下面的向上取整
                float ft = ((float)s / loadFactor) + 1.0F;
                //判断是否大于最大值,正常来说取值为(int)ft,舍弃了小数位
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                //如果m的下一次的扩容阈值大于 原本map的扩容阈值
                if (t > threshold)
                	//使用t计算出新的扩容阈值赋值给threshold  一个最靠近t的2的幂次方数
                    threshold = tableSizeFor(t);
            }
            //table不为空,则已经初始化过了, 对table进行扩容
            else if (s > threshold)
                resize();
            //遍历m
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            	//获取m的 key value 放入map中
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

接着看到remove方法

	public V remove(Object key) {
        Node<K,V> e;
        //如果key存在map中,返回对应的value,否则返回null
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }
    public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null;
    }
    //matchValue为true,则需要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;
        //table不为空 ,table长度大于0 , 使用hash计算出的table的位置上不为空
        //p为该hash值对应的table的位置上的节点
        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;
            //判断p的hash值和p的key 和 被移除的节点的hash和key  是否相同
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //将p赋值给node
                node = p;
            //接着如果p的下一个节点e不为空,则接着寻找链表或者红黑树
            else if ((e = p.next) != null) {
            	//如果是红黑树,从树上找到该节点返回
                if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                //遍历链表寻找该节点
                else {
                    do {
                    	//判断e的hash值和p的key 和 被移除的节点的hash和key  是否相同
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            //将e赋值给node
                            node = e;
                            //找到了就跳出
                            break;
                        }
                        //p记录着被移除的节点的上一个节点
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            //如果node 不为空, 则找到了被移除的节点
            //如果matchValue 为true, 则还要判断节点的value 和 入参的 value 是否一样 才会移除该节点
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                //如果是红黑树,从树上移除该节点
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                //如果node 和 p 相等 ,则说明被移除的节点是table位置上的头节点, 直接将table位置上的节点赋值为下一个节点即可
                else if (node == p)
                    tab[index] = node.next;
                //被移除节点是table位置上的非头的节点
                else
                	//p此时是被移除的节点的上一个节点
                	//直接将p的下一个节点指向被移除节点的下一个节点
                    p.next = node.next;
                //modCount加一 size减一
                ++modCount;
                --size;
                //空方法,交由子类实现
                afterNodeRemoval(node);
                //返回被移除的节点
                return node;
            }
        }
        return null;
    }

看到get方法

	public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
		
		//table不为空,table长度大于0, 使用hash计算出table位置上节点(first)不为空
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            //如果first的 hash 和 key  和 入参的 hash key相同  直接返回first
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            //如果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;
    }

最后来看下EntrySet的iterator方法。

    public final Iterator<Map.Entry<K,V>> iterator() {
            //实例化一个新的EntryIterator对象
            return new EntryIterator();
    }
    //继承了HashIterator
    final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {
	    //调用父类的nextNode方法
        public final Map.Entry<K,V> next() { return nextNode(); }
    }
	abstract class HashIterator {
        Node<K,V> next;        // next entry to return
        Node<K,V> current;     // current entry
        int expectedModCount;  // for fast-fail
        int index;             // current slot
		
        HashIterator() {
        	//expectedModCount被赋值为modCount
            expectedModCount = modCount;
            //t被赋值为table
            Node<K,V>[] t = table;
            current = next = null;
            index = 0;
            //如果table不为空且HashMap中含有元素
            if (t != null && size > 0) { // advance to first entry
            	//遍历t , 从t中找到一个不为空的节点赋值给next,然后跳出循环
                do {} while (index < t.length && (next = t[index++]) == null);
            }
        }
		
        public final boolean hasNext() {
            return next != null;
        }
		
        final Node<K,V> nextNode() {
            Node<K,V>[] t;
            //e被赋值为next
            Node<K,V> e = next; 
            //如果modCount 和 expectedModCount不等 ,抛异常 
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            //将current赋值为e, 将next赋值为下一个节点, 如果next不为空 ,if不成立,直接返回e 
            if ((next = (current = e).next) == null && (t = table) != null) {
            	//如果next为空,寻找t的后面的位置上一个不为空的节点赋值给next
                do {} while (index < t.length && (next = t[index++]) == null);
            }
            return e;
        }
		
        public final void remove() {
        	//获取current
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            //如果modCount 和 expectedModCount不等 ,抛异常 
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            //current 置为空
            current = null;
          	//获取当前节点的key
            K key = p.key;
            //从HashMap中移除该节点 modCount加一了
            removeNode(hash(key), key, null, false, false);
            //修改expectedModCount为modCount  (当使用Iterator遍历过程中,使用该方法移除元素,会修改expectedModCount 和modCount保持一致)
            expectedModCount = modCount;
        }
    }

到此,HashMap的主要源码就解析完毕了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值