HashMap详解和原理

一、概述


HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长.。HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。

线程不安全

可以接受null键值和值,而Hashtable则不能;

查找效率非常高,因为它使用Hash表对进行查找,可直接定位到Key值所在的桶中;

二、主要参数

1、容量(数组长度):初始长度为16,其次在自动扩展或手动初始化时,长度必须是2的幂。

2、阀值(threshold)=容量(capacity,初始为16)*加载因子(load factor默认为0.75)=12,即添加第13 个键值对                        <Key,Value>的时候,map的容量扩充一倍

         (为啥容量为16而加第十三个时就要扩充容量:尽可能第减少桶中的Entry<Key,Value>链表的长度,以提高HashMap的                  存取性能)

三、数据结构

主要为:数组+链表(+红黑树)(右图:当链表长度超过 8 时,链表转换为红黑树。)。

为什么转换为红黑树:

    链表的长度非常长时,查找链表操作的时间复杂度是很高的,为O(n);

   

四、工作原理

HashMap<Integer, Integer> hashMap = new HashMap<>();

HashMap 底层是 hash 数组和单向链表实现,数组中的每个元素都是链表,由 Node 内部类(实现 Map.Entry<K,V>接口)实现,HashMap 通过 put & get 方法存储和获取

   注意:第二次在同一位置put会覆盖原来的value。

1、存储对象(详细流程代码):

    ①、调用 hash(key.hashcode) 方法计算 K 的 hashcode 值,然后结合数组长度,计算得数组下标(即在哪个桶);

  /**
     * 将<Key,Value>键值对存到HashMap中,如果Key在HashMap中已经存在,那么最终返回被替换掉的Value值。
     * Key 和Value允许为空
     */
    public V put(K key, V value) {
        
    	//1.如果key为null,那么将此value放置到table[0],即第一个桶中
    	if (key == null)
            return putForNullKey(value);
    	//2.重新计算hashcode值,
        int hash = hash(key.hashCode());
        //3.计算当前hashcode值应当被分配到哪一个桶中,获取桶的索引
        int i = indexFor(hash, table.length);
        //4.循环遍历该桶中的Entry列表
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //5. 查找Entry<Key,Value>链表中是否已经有了以Key值为Key存储的Entry<Key,Value>对象,
            //已经存在,则将Value值覆盖到对应的Entry<Key,Value>对象节点上
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//请读者注意这个判定条件,非常重要!!!
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        //6不存在,则根据键值对<Key,Value> 创建一个新的Entry<Key,Value>对象,然后添加到这个桶的Entry<Key,Value>链表的头部。
        addEntry(hash, key, value, i);
        return null;
    }
 
 
    /**
     * Key 为null,则将Entry<null,Value>放置到第一桶table[0]中
     */
    private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }

    ②、调整数组大小(当容器中的元素个数大于 阀值 时,容器会进行扩容resize 为 2的n次幂)

   步骤:

     1. 申请一个新的、大小为当前容量两倍的数组;

     2. 将旧数组的Entry[] table中的链表重新计算hash值,然后重新均匀地放置到新的扩充数组中;

     3.  释放旧的数组;

2、获取对象

    ①、调用 hash(key.hashcode) 方法计算 K 的 hashcode 值,从而获取该键值,所链表的数组下标(即在哪个桶);

    ②、顺序遍历链表,equals()方法查找相同 Node 链表中 K 值对应的 V 值。

public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        //遍历列表
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

其他

1、.containsKey(Object key)方法,返回值为boolean,用于判断当前hashmap中是否包含key对应的key-value;

      containsValue(Object value)方法,返回值为boolean,用于判断当前hashmap中是否包含value对应的ke

2. 键值对Entry<Key,Value>的移除----remove(key)方法的实现

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值