JAVA集合之HashMap详解

HashMap 是实现与Map接口 java集合框架中重要的集合 底层数据结构是 数组+链表+红黑树
非线程安全
在这里插入图片描述

属性解释

	//默认初始化数组容量 默认 16 
	static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
	// 最大数组容量 2^30 次方个
	static final int MAXIMUM_CAPACITY = 1 << 30;
	// 负载因子 默认0.75 
	static final float DEFAULT_LOAD_FACTOR = 0.75f;
	// 树化阈值
	static final int TREEIFY_THRESHOLD = 8;
	// 解除树化的阈值
	static final int UNTREEIFY_THRESHOLD = 6;
	// 最小树化容量
	static final int MIN_TREEIFY_CAPACITY = 64;
	//  内部类Node 是哈希冲突后的 链表结点
	static class Node<K,V> implements Map.Entry<K,V>
	// 一个哈希表 
	transient Node<K,V>[] table;
	
	transient Set<Map.Entry<K,V>> entrySet;
	// 元素个数
	transient int size;
	//修改次数
	transient int modCount;
	// 扩容阈值  thershold = capacity*loadFactor = 16*0.75=12
	int threshold; 
	// 负载因子 0.75
	final float loadFactor;

构造方法解释

  所有的构造方法,都没有new出空间,进行了赋值 
	// 传入数组容量 &&  负载因子
	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);//tableSizeFor 方法获取第一个大于或等于initialCapacity的2的指数次方的数 这样保证了哈希表的长度永远是2的指数次
    }
    // 传入数组容量
	public HashMap(int initialCapacity) {
	      this(initialCapacity, 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);
    }

put 方法

	// 传入key-value
	 public V put(K key, V value) {
	 	// 将key计算hash值后和key,value 传入putVal方法
        return putVal(hash(key), key, value, false, true);
    }
    // 实现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;
        // 将table赋值给tab 并且判断是否为空
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length; //如果为空 就初始化这个表 延迟初始化 有利于内存管理 
            //n=tab.length  resize方法 是初始化表 或者执行扩容。 第一次put一个元素时或者到达扩容阈值 执行此方法
        if ((p = tab[i = (n - 1) & hash]) == null)// 计算hash 得到 一个索引  (n-1)&hash 得到一个索引
            tab[i] = newNode(hash, key, value, null); //如果索引没有元素 就直接new一个Node 
        else { // 发生hash 冲突 因为当前索引已经有元素了 就执行链化 跟在当前索引元素后面
            Node<K,V> e; K k; 
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //如果p的ash 等于 你需要put的key的hash 或者 key相等了 就是判断key是是否已经存在了
                e = p;//用e记录 p这个结点 p是hash计算得到索引的哈希表的结点
            else if (p instanceof TreeNode) // 是否已经树化了
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); // 执行putTreeVal方法
            else {// key 不相等  也没有树化 就需要链接到链表上
                for (int binCount = 0; ; ++binCount) { 
                    if ((e = p.next) == null) { // e 记录当前结点的一下一个 判断是否为空
                        p.next = newNode(hash, key, value, null); // 等于空 就先new一个
                        if (binCount >= TREEIFY_THRESHOLD - 1) // 0 ----7 = 8-1 判断链表是否有了8个结点
                             treeifyBin(tab, hash); // 树化 判断 数组长度是否小于64 小则扩容 大就树化
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                      // 如果发现有key 相等的情况就退出
                        break;
                    p = e; // 一直往下遍历
                }
            }
            //如果 key相等 就覆盖value 值 返回 旧的值
           if (e != null) { 
                V oldValue = e.value;  
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount; // 修改次数加1
        if (++size > threshold) // 容量是否达到扩容阈值 thershold = capacity*loadFactor = 16*0.75=12
            resize(); // 扩容
        afterNodeInsertion(evict);
        return null;
    }

get方法

	// 传入键 获取value
	public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    // 返回一个结点 传入hash  和key
	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) {
       //表不为空 获取索引 
         // 第一个key 就是我们需要get 的key
            if (first.hash == hash && // always check first node
                ((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 { // do while 循环找 到相同的key 返回结点
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        // HashMap中没有 key相同的结点
        return null;
    }

replace 方法

// 将key的oldValue 替换 newValue
 public boolean replace(K key, V oldValue, V newValue) {
        Node<K,V> e; V v;
        if ((e = getNode(hash(key), key)) != null &&
            ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
            e.value = newValue;
            afterNodeAccess(e);
            return true;
        }
        return false;
    }
    // 将key的value 替换
    public V replace(K key, V value) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) != null) {
            V oldValue = e.value;
            e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
        return null;
    }

remove 方法

	// 移除一个key 的结点
	 public boolean remove(Object key, Object value) {
   	
        return removeNode(hash(key), key, value, true, true) != null;
    }
   // 接收参数 hash  key 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) {
            // 判断表不为空 && hash的第一个元素不为空
            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; // 第一个就找到key相同的结点了
            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;
                            // 找到key 的结点 用node记录
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            // 已经找到 所以node不等于NULL
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                if (node instanceof TreeNode) // 如果已经树化 就用removeTreeNode 
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                else if (node == p) // 如果node 是第一个 就顺位继承
                    tab[index] = node.next;
                else // 顺位继承
                    p.next = node.next;
                ++modCount; //修改次数加1
                --size; // 元素-1
                afterNodeRemoval(node);
                return node;
            }
        }
       // 找不到key 
        return null;
    }

扩容方法

// 扩容和初始化方法
 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) { // 如果旧的容量大于0 
            if (oldCap >= MAXIMUM_CAPACITY) { // 如果旧的容量已经大于容量最大值了
                threshold = Integer.MAX_VALUE; // 扩容阈值就等于 int的最大值
                return oldTab; // 不扩容了
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                     // 如果当前容量的两倍 小于 最大值, 并且 大于默认的初始容量 
                     //扩容两倍
                newThr = oldThr << 1; 
        }
        else if (oldThr > 0) // 扩容阈值大于0 说明不是初始化的情况
            newCap = oldThr;
        else {               // 初始化的情况 
            newCap = DEFAULT_INITIAL_CAPACITY; // 新的容量就等于默认容量 16
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 
            // 扩容阈值就等 =  负载因子 * 默认容量 = 0.75 * 16 = 12
        }
        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]; // 初始化一个newCap容量的Node数组
        table = newTab;// 延迟初始化
        if (oldTab != null) { // 不是初始化的情况 而是正常的扩容情况 执行这个if
            for (int j = 0; j < oldCap; ++j) { // 遍历到 oldCap 容量
                Node<K,V> e; //临时结点
                if ((e = oldTab[j]) != null) { // 如果这个索引位置 不等于空
                    oldTab[j] = null; //帮助gc 回收
                    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 { // 链化的情况
					// 低位链表,存放在扩容之后的数组下标位置,与当前数组的下标位置一致。
                        Node<K,V> loHead = null, loTail = null;
                        
                    // 高位链表,存放在扩容之后的数组的下标位置的胃当前数组下标位置+ 扩容之前数组的长度
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next; // 记录e的下一个
							 //hash -> ... 1 1111 
							 // hash-> ....0 1111
							 //            1 0000
							// 只会得到第五位是0 或者1  情况 如果是0 那么就存放低位链表 反正就是高位链表
                            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);
                        // 如果低位链表 有数据 如果在原来的链表中就需要将最后一个设置为 null
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值