HashMap

一种经典的kay-value结构的Map,常用于缓存数据,实现在jdk1.8有升级

内部实现:

1.8:底层是数组+链表+红黑树

1.7:底层是数组+链表

核心参数:

DEFAULT_INITIAL_CAPACITY:默认初始化容量,默认值:1<<4

MAXIMUM_CAPACITY:最大容量,默认值:2<<30

DEFAULT_LOAD_FACTOR:默认负载因数,默认值:0.75

MIN_TREEIFY_CAPACITY:最小树容量,默认值:64

TREEIFY_THRESHOLD:树化阈值,默认值:8

UNTREEIFY_THRESHOLD:取消树化阈值,默认值:6

常用方法

put(key, value)

1、判断hash桶是否为空或null,是则resize()初始化

2、根据key的hash值计算出需要放入的位置i,i为空则直接放入,有值则通过hashCode()和equals()判断key是否相等,相等则更新value

3、不等则判断是否为链表,是则检查链表长度是否超过8,超过则转为红黑树再插入,没有则遍历比较key值,看是更新value还是尾插

4、如果是红黑树则比较key值后,看是更新value还是尾插

5、检查hash桶是否需要扩容

如何扩容

提炼后的扩容代码


void resize(int newCapacity) {   //传入新的容量
	Entry[] oldTable = table;    //引用扩容前的Entry数组
	int oldCapacity = oldTable.length;     
	if (oldCapacity == MAXIMUM_CAPACITY) {  //扩容前的数组大小如果已经达到最大(2^30)了
        threshold = Integer.MAX_VALUE; //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了
        	return;
     	}
  
	Entry[] newTable = new Entry[newCapacity];  //初始化一个新的Entry数组
     	transfer(newTable);                         //!!将数据转移到新的Entry数组里
     	table = newTable;                           //HashMap的table属性引用新的Entry数组
     	threshold = (int)(newCapacity * loadFactor);//修改阈值
 }


 void transfer(Entry[] newTable) {
     Entry[] src = table;                   //src引用了旧的Entry数组
     int newCapacity = newTable.length;
     for (int j = 0; j < src.length; j++) { //遍历旧的Entry数组
         Entry<K,V> e = src[j];             //取得旧Entry数组的每个元素
         if (e != null) {
             src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象)
             do {
                 Entry<K,V> next = e.next;
                 int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置
                 e.next = newTable[i]; //标记[1]
                 newTable[i] = e;      //将元素放在数组上
                 e = next;             //访问下一个Entry链上的元素
             } while (e != null);
         }
     }
} 

每次扩容2倍

扩容时会重新hash,能有效的打散原链表上的元素

posted @ 2022-08-03 22:54  mudongs  阅读(29)  评论(0)    收藏  举报