hashmap原理总结

HashMap的数据结构




哈希表-拉链法,我们可以理解为“链表的数组” ,如图:

首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next

HashMap的存取实现

初始:table初始大小是的2的初始容量次幂

   public HashMap( int initialCapacity,  float loadFactor) {
        .....
         int capacity = 1;
         while (capacity < initialCapacity)
            capacity <<= 1;                           //左移代表乘2,右移代表除2
         this. loadFactor = loadFactor;
         threshold = ( int)(capacity * loadFactor);
         table = new Entry[capacity];
        init();
    }

put

  public V put(K key, V value) {
         if (key ==  null)
             return putForNullKey(value);  //null总是放在数组的第一个链表中
         int hash =  hash(key.hashCode());
         int i =  indexFor(hash,  table. length);//找到在数组中的位置
         //遍历数组右侧链表
         for (Entry<K,V> e =  table[i]; e !=  null; e = e. next) {
            Object k;
             //如果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++;
         addEntry(hash, key, value, i);
         return  null;

    }

void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e =  table[bucketIndex];
     table[bucketIndex] =  new Entry<K,V>(hash, key, value,  e);  //参数e, 是Entry.next
     //如果table数组的size超过threshold,则扩充table大小。再散列
     if ( size++ >=  threshold)
            resize(2 *  table. length);
}

HashMap里面设置一个因子,随着map的存储的size越来越大,Entry[]会以一定的规则加长长度。




get

  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;
}




null key的存取

null key总是存放在Entry[]数组的第一个元素。HashMap是泛型类,key和value可以为任何类型,包括null类型。key为null的键值对永远都放在以table[0]为头结点的链表中,当然不一定是存放在头结点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;
    }
     private V  getForNullKey() {
         for (Entry<K,V> e =  table[0]; e !=  null; e = e. next) {
             if (e. key ==  null)
                 return e. value;
        }
         return  null;
    }

hashMap为什么存储2的n次幂个空间?

确定数组index:hashcode % table.length取模,HashMap存取时,都需要计算当前key应该对应Entry[]数组哪个元素,即计算数组下标;算法如下:

    /**
     * Returns index for hashcode h.
     */
     static  int  indexFor( int  hashcodeint length) {
         return  hashcode  & (length-1);
    }
只有全是1, hashcode 进行按位与才是最均匀的

HashMap是根据key的hash值决定key放到哪个桶中,通过hash&(length-1)公式计算得出,2的n次方一定是最高位1其它低位是0,这样减1的时候才能得到01111这样都是1的二进制。这样(n - 1) & hash的值是均匀分布的,可以减少hash冲突。


 

扩容

当hash数组中的元素个数超出了最大加载因子和容量的乘积时,要对hashMap进行扩容,扩容过程存在于hashmap的put方法中, 扩容过程始终以2次方增长

3. 解决hash冲突的办法

  1. 开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
  2. 再哈希法
  3. 链地址法
  4. 建立一个公共溢出区

Java中hashmap的解决办法就是采用的链地址法。

java8的性能改善

当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能

put方法

table变量:HashMap的底层数据结构,是Node类的实体数组,用于保存key-value对;
capacity:并不是一个成员变量,但却是一个必须要知道的概念,表示容量;
size变量:表示已存储的HashMap的key-value对的数量;
loadFactor变量:装载因子,用于衡量满的程度;
threshold变量:临界值,当超出该值时,表示table表示该扩容了;

threshold=capacity*loadFactor(默认是0.75)

当size>threshold则扩容

一. put方法

HashMap使用哈希算法得到数组中保存的位置,然后调用put方法将key-value对保存到table变量中。我们通过图来演示一下存储的过程。

resize原理

hashmap线程不安全的会导致什么问题?

JDK1.7 HashMap线程不安全体现在:死循环、数据丢失
JDK1.8 HashMap线程不安全体现在:数据覆盖

HashMap为什么线程不安全-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值