- 1、HashMap
- 基于哈希表的 Map接口的实现
- 对HashMap实例有影响其性能的两个参数:初始容量和负载因子(哈希表中的存储桶的数目:table的长度)
- 结构:
- JDK1.7数组+链表
- JDK1.8数组+链表/红黑树
- 2、 在创建HashMap对象时
-
a.初始化工作
-
public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
将负载因子设置为0.75
table=null
临界值=0 -
b.在第一次添加数据时,会将数组容量设置为16(JDK1.7默认初始容量为16,JDK1.8默认初始容量为0,第一次添加时变为16),并且计算出临界值为12:((int)16*0.75)
-
c.在超过hash表的临界值时,会先进行添加数据的操作,在进行扩容(扩容规则是原容量的2倍,新的临界值也是原来的2倍)
if (++size > threshold)
resize(); -
d.扩容完成后,会将旧数组中的数据,转移到新数组中(会重新根据hash值和新数组长度进行计算新的索引位置)
-
e.在添加数据时,如果一个桶中的链表长度大于8,并且数组长度达到64,则将当前链表结构变为红黑树结构
如果当前桶内已经是树结构了,则按照树结构的方式去添加数据
-
- 3、 在添加数据时
-
final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) {//只有第一次添加时,不进入if的,以后的每次扩容都会进入到if中 if (oldCap >= MAXIMUM_CAPACITY) {//如果就容量大于最大值,则采用int范围的最大值作为临界值 threshold = Integer.MAX_VALUE; return oldTab; } //对旧容量进行2倍操作,并赋值给新容量 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) //将临界值也更新为原来的2倍 newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults //如果是第一次添加数据,则初始的容量和初始的临界值都是通过常量进行赋值 newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr;//将新临界赋值给对象中的临界值属性 @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//采用新容量创建hash表 table = newTab;//将新创建的hash表赋值给table (table就有容量了) if (oldTab != null) { //主要是将旧数组中的数据,循环添加到新数组中,会重新分配空间 for (int j = 0; j < oldCap; ++j) { 省略... } }
HashMap与HashTable的区别
- 可以存储的值不同
- HashMap:key和value都可以存null
- HashTable:key和value都不可以存null
- 存储方式不同
- HashMap:JDK1.7数组+链表,JDK1.8数组+链表/红黑树
- HashTable:数组+链表
- 默认容量不同:
- HashMap:JDK1.7默认初始容量为16,JDK1.8默认初始容量为0,第一次添加时变为16
- HashTable:默认容量:11(质数为宜)
- 扩容不同
- HashMap:
- ①当总元素个数超过 容量 * 负载因子(0.75)时,扩容为原来 2 倍,并重新排列元素
- ②当单个链表的长度达到8时,继续向该链表结尾添加数据,此时:
- 如果总容量小于64,那么扩容为原来的2倍,并重新排列元素
- 如果总容量大于64,那么将该链表转为红黑树(当单个位置的长度小于6时转回链表)
- HashTable:当总元素个数超过 容量 * 负载因子(0.75)时,扩容为原来 2 倍+1并重新散列
- HashMap:
- 线程安全性:
- HashMap:线程不安全
- HashTable :线程安全
- 效率不同:
- HashMap:效率快,不安全
- HashTable :效率慢,因为加锁