【HashMap-1.7】-从源码解析-通俗易懂【扩容】【线程不安全】【put&get原理】【遍历删除】


要点归纳

  • 数据结构
    • 1.7 HashMap 的数据结构 为 数组 + 链表 ,[1.8引用数组+链表+红黑树]
  • 初始化
    • 构造方法
    • 调用初始化
  • put 方法
    • 主要过程
    • 判断分配空间
    • key为空处理
    • 计算索引位
    • key冲突 处理过程
    • addEntry 、是否扩容
  • 扩容
    • 扩容过程
    • 环形链表问题

之前了解过的可按照上面提到的回想下,是否都掌握。



定义

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{
    ...  }

可看到 HashMap 继承了 AbstractMap ,实现了 Map、Cloneable、Serializable
Cloneable:标记为colone、Serializable:标记为可被序列^化


来看下 定义的属性

	/**
	* 定义默认长度 16     2^4
	*/
	static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
	/**
	* 最大长度         2^30
	*/
	static final int MAXIMUM_CAPACITY = 1 << 30;
	/**
	* 默认加载因子 [size >= 阈值(容量*加载因子) 则会进行扩容]
	*/
	static final float DEFAULT_LOAD_FACTOR = 0.75f;
	/**
	* 空的Entry 数组对象 
	*/
	static final Entry<?,?>[] EMPTY_TABLE = {
   };
	/**
	* 用于存放key value的对象 [还存储了当前对象的hash值 以及 next Entry *hash冲突时会形成链表结构]
	*/
	transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
	/**
	* 当前元素个数
	*/
	transient int size;
	/**
	* 当前数组的阈值
	*/
	int threshold;
	/**
	* 当前加载因子
	*/
	final float loadFactor;
	/**
	* 记录当前集合被修改的次数
	*/
	transient int modCount;

构造方法

	/**
	* 无参构造 则会取 默认长度 及 默认加载因子 调用下面的构造方法
	*/
	public HashMap() {
   
	        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
	    }
	
	/**
	* 手动定义长度 及 默认加载因子 调用下面的构造方法
	*/    
	public HashMap(int initialCapacity) {
   
	        this(initialCapacity, DEFAULT_LOAD_FACTOR);
	    }
    
    /**
	* map类型的参数 构造方法 
	*/
	public HashMap(Map<? extends K, ? extends V> m) {
   
	       this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
	                     DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
	       inflateTable(threshold);
	
	       putAllForCreate(m);
	  }
 	/**
	* 指定长度 及 加载因子
	*/
   	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;
	       threshold = initialCapacity;
	       init();
    }

以上则会发现,调用构造方法,虽然定义了 长度 及 加载因子,但并未进行初始化。
此时 table 对象 仍然为 EMPTY_TABLE


put方法

    public V put(K key, V value) {
   
        //校验 当前 数组为空 则进行初始化 [✨此过程为要点]
        if (table == EMPTY_TABLE) {
   
            inflateTable(threshold);
        }
        //key 为 null 时,进行针对 null key 的赋值逻辑 [✨此过程为要点]
        if (key == null)
            return putForNullKey(value);
        //计算 key 对应的 hash 值  
        int hash = hash(key);
        //根据 hash 值 计算出 索引值 i [该key 对应的value 应放在 数组的索引位置] [✨此过程为要点]
        int i = indexFor(hash, table.length);
        //遍历 i 索引对应的 entry对象,如果hash 及 key 相等[引用 或 值 相等]
        //则将 value 替换 oldValue 并将 oldValue返回  [✨此过程为要点]
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
   
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
   
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
		//记录修改次数
        modCount++;
        //若上方没有匹配到相同的key 添加 Entry 对象
        addEntry(hash, key, value, i);
        return null;
    }

inflateTable 初始化方法

    private void inflateTable(int toSize) {
   
        // Find a power of 2 >= toSize
        //返回 第一个 大于或等于 参数 2 的 幂值 16 -> 16 、 17 -> 32
        //为什么一定要取 2的幂 后续会讲解 [✨此过程为要点]
        int capacity =
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值