HashMap源码个人理解

#HashMap源码个人理解 HashMap extends AbstractMap implements Map

Map接口提供以下常用方法:

Map

  • size()
  • isEmpty()
  • containsKey()
  • containsValue()
  • get()
  • put()
  • remove()
  • putAll()
  • clear()
  • keySet()
  • values()
  • equals()
  • hashCode()
  • I Entuty<K,V>

AbstractMap实现了上述常用方法。

HashMap在AbstractMap的基础上添加并优化了一些常用方法, 并重写了AbstractMap的部分方法, 使HashMap具有自己的特性。下面是一些HashMap的特性:

  1. loadFactorinitialCapacity 在构造函数中传入该值。 initialCapacity为初始容量, 默认为1<<4 = 16 个。注意:当Map容量大于2^32-1个是 Map数量不再增加。(此时估计内存也会被撑爆)。

    loadFactor为Map膨胀系数, 当map达到一定容量之后每次增加计算方法为:1、(putAll时这样计算)int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); 2、添加元素时 resize方法 resize(2 * table.length);一次扩容两倍。这里的容量由loadFactor决定, 计算公式为:threshold = (int) Math.min(capacity * loadFactor, 2^31);

  2. resize()方法

     //重新扩容当前的集合
     void resize(int newCapacity) {
         Entry[] oldTable = table;
         int oldCapacity = oldTable.length;
         if (oldCapacity == MAXIMUM_CAPACITY) {
             threshold = Integer.MAX_VALUE;
             return;
         }
    
         Entry[] newTable = new Entry[newCapacity];
     	//将老集合的数据传到新的集合
         transfer(newTable, initHashSeedAsNeeded(newCapacity));
         table = newTable;
     	//重新计算下一次需要扩容的数量
         threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
     }
    

这个是transfer方法

 //将老数据转移到新的数组里
 void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next;  //找到下一个
            if (rehash) {
				//重新计算hash
                e.hash = null == e.key ? 0 : hash(e.key);
            }
			//根据新数组的长度, 重新计算元素的在数组中的index
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];//**如果当前节点已经有数据了, 就把这个数据放到该entity的next节点上, 这里很重要, 这就是解决hashmap  hash相同的解决方案**
            newTable[i] = e;
            e = next;//遍历整个链表
        }
    }
}
  1. 以数组作为存储的容器。

     //创建一个Entity数组, 所有的数据都是存在这个数组里的, 这个数组的index根据key的hash计算而来。
     transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
     //计算数组index的方法:
     //根据key的hash 和length的位运算找到index
     static int indexFor(int h, int length) {
     // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
     return h & (length-1);
    

    }

  2. 所有的put、get等都先判断key值是否等于null。因为null值的index = 0 如:e.hash = null == e.key ? 0 : hash(e.key);

     //根据key找entity,根据key就可以确定数组的index. 只需要遍历所有的next节点找到相同的key即可。
     final Entry<K,V> getEntry(Object key) {
         if (size == 0) {
             return null;
         }
    
         int hash = (key == null) ? 0 : hash(key);
         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 != null && key.equals(k))))
                 return e;
         }
         return null;
     }
     //put元素 先确定这个元素的index, 然后找这个元素上是不是已经有节点了, 如果有节点了直接插入值,否则创建新元素
     public V put(K key, V value) {
         if (table == EMPTY_TABLE) {
             inflateTable(threshold);
         }
         if (key == null)
             return putForNullKey(value);
         int hash = hash(key);
         int i = indexFor(hash, table.length);
     	//如果当前节点已经有值了, 那么就在这个节点追加元素,否则创建新元素
         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++;
         addEntry(hash, key, value, i);
         return null;
     }
    
     //判断容器是否需要重新扩容
     void addEntry(int hash, K key, V value, int bucketIndex) {
         if ((size >= threshold) && (null != table[bucketIndex])) {
             resize(2 * table.length);
             hash = (null != key) ? hash(key) : 0;
             bucketIndex = indexFor(hash, table.length);
         }
    
         createEntry(hash, key, value, bucketIndex);
     }
    
     //将当前元素插入到数组中, 如果数组中已经有值了,那么将该元素插入到尾端
     void createEntry(int hash, K key, V value, int bucketIndex) {
         Entry<K,V> e = table[bucketIndex];
         table[bucketIndex] = new Entry<>(hash, key, value, e);
         size++;
     }
    

转载于:https://my.oschina.net/xbding/blog/700107

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值