HashMap存储分析

通过代码  transient  Entry[]  table ; 可以看出 有一个Entry数组。
static  class  Entry<K,V>  implements  Map.Entry<K,V> {
         final  K  key ;
        V  value ;
        Entry<K,V>  next ;
         final  int  hash ;
}
通过上面这段代码可以看出:
      内部类 Entry中有四个属性信息,分别为key,value,next指针,hash值,即每一个Entry对象都有这四个信息。
通过 for  (Entry<K,V> e =  table [i]; e !=  null ; e = e. next  )  这个for循环代码可以看出:
     table数组中,每一个位置都存有一些Entry对象信息,而这些信息用next连接了起来,就形成了一个链表,所以table数组中的每一个位置都存有Entry对象的链表(当然了,也可能是null)。

再看一下put代码:
public  V put(K key, V value) {
         if  (key ==  null )
             return  putForNullKey(value);
         //计算用自己的hash方法计算hash值
         int  hash = hash(key.hashCode());
         //用计算出的hash值与table的长度进行与运算,计算 出要put的键值对存放的table数组的位置i
         int  i = indexFor(hash,  table  . length  );
        //循环遍历table数组位置i下的链表信息
        for  (Entry<K,V> e =  table [i]; e !=  null ; e = e. next ) {
            Object k;
            //如果要存储的key的hash值与table数组位置i下的某个链表节点的hash值相等,
            //并且,key值也相等,那么就执行替换操作
             if  (e.  hash  == hash && ((k = e.  key ) == key || key.equals(k))) {
                V oldValue = e.  value ;
                e.  value  = value;
                e.recordAccess(  this );
                 return  oldValue;
            }
        }
         modCount ++;
         //如果循环遍历之后,没有找到hash和key都相同的Entry节点,那么就在table数组的位置i处,
        //插入一个Entry节点信息,addEntry方法中有四个参数,分别是hash值,key,value,table数组的位置i
        addEntry(hash, key, value, i);
         return  null ;
    }

再看一下addEntry方法:
void  addEntry( int  hash, K key, V value,  int  bucketIndex) {
     //在插入之前,首先将table数组的位置i处的所有Entry节点信息都保存到e对象中,因为table数组的每一个位置都是一个Entry对象。
     Entry<K,V> e =  table [bucketIndex];
     //然后新建一个Entry对象,然后将这个新建的Entry对象再赋值给table数组的位置i处。
      table [bucketIndex] =  new  Entry<K,V>(hash, key, value, e);
      if  ( size ++ >=  threshold )
         resize(2 *  table .  length );
    }

单独拿出 new  Entry<K,V>(hash, key, value, e);来说一说:
     新建一个Entry对象,传入的参数有四个,分别是根据key计算出来的hash,key,value,以及table数组的位置i处的所有Entry节点信息。

看一下Entry的构造方法
        Entry(  int  h, K k, V v, Entry<K,V> n) {
             value  = v;
             next  = n;
             key  = k;
             hash  = h;
        }

根据  next  = n;可以知道,我新建一个Entry对象节点,然后将next指针指向了传入的参数n(即上面保存table[bucketIndex]的e对象节点),我觉得新建的这个Entry对象节点应该会指向原来的Entry对象的头节点处。 此时table[i]的位置的那个Entry对象应该改变了,变成刚刚新创建的那个Entry对象节点了。当再次put的时候,再次执行这个for循环的时候 for  (Entry<K,V> e =  table [i]; e !=  null  ; e = e. next  )  只是table[i]的值变化了一下,table[i]处的Entry链表长度变化了一下。


分析完put方法之后,再分析一下get方法:
      public  V get(Object key) {
         if  (key ==  null )
             return  getForNullKey();
        //计算key值的hash值,与put方法的一样
         int  hash = hash(key.hashCode());
        //用 indexFor(hash,  table .  length )方法进行与运算,然后找到了要在table数组取值的位置
        //循环遍历table[i]处的Entry链表节点
         for  (Entry<K,V> e =  table [ indexFor(hash,  table .  length )];
             e !=  null ;
             e = e.  next ) {
            Object k;
            //如果key值的hash与table数组中存储的Entry对象e的hash值相等,同时key值相等,那么,就说明找到了,并返回value值。
             if  (e.  hash  == hash && ((k = e.  key ) == key || key.equals(k)))
                 return  e.  value ;
        }
         return   null ;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值