马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
主要过一遍HashMap中的常量、构造方法、put方法
当我们调用put时,实际上就是调用putVal- public V put(K key, V value) {
- return putVal(hash(key), key, value, false, true);
- }
复制代码 putVal
- /**
- * @param onlyIfAbsent 若为true,插入重复key的值时,不改变已存在的value
- * @param evict 若为false,则表为创建模式(在HashMap中因该没啥用,LinkedhashMap需要注意)
- */
- final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
- Node<K,V>[] tab;
- Node<K,V> p; // p在后边用来暂存冲突位置上的元素
- int n, i; // n为table.length
- // 1. 新创建的HashMap先初始化
- if ((tab = table) == null || (n = tab.length) == 0)
- n = (tab = resize()).length;
- // 2. 若指定位置不存在元素(没有hash冲突)
- if ((p = tab[i = (n - 1) & hash]) == null) // n为hashMap的长度,即2的幂次,故:(n-1)&hash = hash%n
- tab[i] = newNode(hash, key, value, null);
- else {
- // 3. 发生冲突时处理逻辑
- Node<K,V> e;
- K k; // 冲突位置上的key
- // 3.1 key相同则覆盖旧值
- if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
- e = p;
- // 3.2 已有很多冲突值(已经转为树了),则put进该冲突树
- else if (p instanceof TreeNode)
- e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
- // 3.3 拉链法存放
- else {
- // 3.3.1 循环冲突链表,将新来的冲突的元素尾插到链表中
- for (int binCount = 0; ; ++binCount) {
- if ((e = p.next) == null) {
- p.next = newNode(hash, key, value, null);
-
- if (binCount >= TREEIFY_THRESHOLD - 1) // TREEIFY_THRESHOLD = 8
- // 链表转红黑树
- treeifyBin(tab, hash);
- break;
- }
- // 如果新插入的元素是与冲突链表上的key相同的,那么就要覆盖这个key对应的value
- if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
- // beark后走【a】处的逻辑
- break;
- p = e;
- }
- }
- // 循环结束后,经历尾插后的e为空
-
- if (e != null) { // 【a】existing mapping for key
- V oldValue = e.value;
- if (!onlyIfAbsent || oldValue == null)
- e.value = value;
- afterNodeAccess(e);// 这方法空的,没啥用,在LinkedHashMap中被重写
- return oldValue;
- }
- }
-
- ++modCount; // 记录被结构修改的次数
- // 如果当前保存的k-v数量(不包含冲突的)大于阈值 则扩容
- if (++size > threshold)
- resize();
- afterNodeInsertion(evict);// 这方法空的,没啥用,在LinkedHashMap中被重写
- return null;
- }
复制代码 resize
初始化或加倍表的大小。
如果为空,则按照字段阈值中持有的初始容量目标分配。
否则,因为我们使用的是2的幂展开,所以每个bin中的元素要么必须保持相同的索引,要么在新表中以2的幂偏移量移动。
- final Node<K,V>[] resize() {
- Node<K,V>[] oldTab = table;
- int oldCap = (oldTab == null) ? 0 : oldTab.length;
- int oldThr = threshold; // 类成员变量threshold,int类型默认值为0
- int newCap, newThr = 0;
- // 1.确定新阈值和新容量
- // 1.1 元素不为空
- if (oldCap > 0) {
- if (oldCap >= MAXIMUM_CAPACITY) { // MAXIMUM_CAPACITY = 1 << 30 = 1073741824
- threshold = Integer.MAX_VALUE;// MAX_VALUE = 2147483647
- return oldTab;
- }
- // newCap扩为原来的2倍 ,若其在[16,1073741824)范围内threshold也翻倍
- else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
- newThr = oldThr << 1;
- }
- // 1.2 元素为空,且阈值有效(可能是调用HashMap(int initialCapacity,float loadFactor)初始化或被清空元素的hashmap)
- else if (oldThr > 0)
- newCap = oldThr;
- // 1.3 新创建的HashMap 容量默认为16,阈值默认为(负载因子*容量)0.75*16=12
- else {
- 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"})
- // 2.扩容
- Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
- table = newTab;
- if (oldTab != null) {
- // 循环老的元素
- for (int j = 0; j < oldCap; ++j) {
- Node<K,V> e;
- if ((e = oldTab[j]) != null) {
- oldTab[j] = null;
- // 2.1 该元素链表中没有存储冲突值,重新计算该元素存储位置,并赋值
- if (e.next == null)
- newTab[e.hash & (newCap - 1)] = e;
-
- // 2.2 如果该元素保存到冲突值很多 以至于链表已经转成红了
- else if (e instanceof TreeNode)
- ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
-
- // 2.3 有产生冲突的值,但还没有转成树
- else { // preserve order
- Node<K,V> loHead = null, loTail = null;
- Node<K,V> hiHead = null, hiTail = null;
- Node<K,V> next;
- do {
- next = e.next;
- // 空键的hash为零,或出现1001 & 0110这种情况
- if ((e.hash & oldCap) == 0) {
- if (loTail == null)
- loHead = e;
- else
- loTail.next = e;
- loTail = e;
- }
- else {
- if (hiTail == null)
- hiHead = e;
- else
- hiTail.next = e;
- hiTail = e;
- }
- } while ((e = next) != null);
- // 将冲突的链表拎出来,放到数组中
- if (loTail != null) {
- loTail.next = null;
- newTab[j] = loHead;
- }
- if (hiTail != null) {
- hiTail.next = null;
- newTab[j + oldCap] = hiHead;
- }
- }
- }
- }
- }
- return newTab;
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
|