HashMap源码解析(JDK8)

素颜马尾好姑娘i 2021-09-25 08:56 599阅读 0赞

HashMap源码解析

    • 结构图
    • 常用变量
    • 内部类
    • 构造方法
    • 常见方法
      • 核心方法hash、resize、tableSizeFor等
      • put
      • get
      • remove
    • 总结

结构图

hashmap

  1. public class HashMap<K,V> extends AbstractMap<K,V>
  2. implements Map<K,V>, Cloneable, Serializable {
  3. ...
  4. }

常用变量

  1. // 序列号 ,序列化使用
  2. private static final long serialVersionUID = 362498820763181265L;
  3. // 默认的初始容量 1<<4 带符号左移 0001 ---> 10000 (2进制) = 16(10进制)
  4. static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
  5. // 最大容量 2的30次方
  6. static final int MAXIMUM_CAPACITY = 1 << 30;
  7. // 加载因子 例如:数组容量为8 当元素数量为6(8*0.75)时,则会触发扩容。
  8. static final float DEFAULT_LOAD_FACTOR = 0.75f;
  9. // 当某个桶节点(桶即为数组的每个格子)数量大于8时,会转换为红黑树。
  10. static final int TREEIFY_THRESHOLD = 8;
  11. //当某个桶节点数量小于6时,会转换为链表,前提是它当前是红黑树结构。
  12. static final int UNTREEIFY_THRESHOLD = 6;
  13. // 整个hashMap中元素数量大于64时,也会进行转为红黑树结构
  14. static final int MIN_TREEIFY_CAPACITY = 64;
  15. // hashmap底层存储数据结构 --数组 transient关键字表示该属性不能被序列化
  16. transient Node<K,V>[] table;
  17. // 将数据转换成set的另一种存储形式
  18. transient Set<Map.Entry<K,V>> entrySet;
  19. // 元素数量
  20. transient int size;
  21. // 统计该map修改的次数
  22. transient int modCount;
  23. // 临界值,也就是元素数量达到临界值时,会进行扩容 (LOAD_FACTOR * INITIAL_CAPACITY)
  24. int threshold;
  25. // 同加载因子
  26. final float loadFactor;

内部类

  1. static class Node<K,V> implements Map.Entry<K,V> {
  2. final int hash;
  3. final K key;
  4. V value;
  5. Node<K,V> next;
  6. Node(int hash, K key, V value, Node<K,V> next) {
  7. this.hash = hash;
  8. this.key = key;
  9. this.value = value;
  10. this.next = next;
  11. }
  12. public final K getKey() { return key; }
  13. public final V getValue() { return value; }
  14. public final String toString() { return key + "=" + value; }
  15. public final int hashCode() {
  16. return Objects.hashCode(key) ^ Objects.hashCode(value);
  17. }
  18. public final V setValue(V newValue) {
  19. V oldValue = value;
  20. value = newValue;
  21. return oldValue;
  22. }
  23. public final boolean equals(Object o) {
  24. if (o == this)
  25. return true;
  26. if (o instanceof Map.Entry) {
  27. Map.Entry<?,?> e = (Map.Entry<?,?>)o;
  28. if (Objects.equals(key, e.getKey()) &&
  29. Objects.equals(value, e.getValue()))
  30. return true;
  31. }
  32. return false;
  33. }
  34. }
  35. // 树节点 注意继承了 LinkedHashMap的Entry,LinkedHashMap的Entry继承了HashMap的Node
  36. // 说明树节点本身也是一种双向链表
  37. static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
  38. TreeNode<K,V> parent; // red-black tree links
  39. TreeNode<K,V> left;
  40. TreeNode<K,V> right;
  41. TreeNode<K,V> prev; // needed to unlink next upon deletion
  42. boolean red;
  43. TreeNode(int hash, K key, V val, Node<K,V> next) {
  44. super(hash, key, val, next);
  45. }
  46. ...// 省略树的相关方法
  47. }

构造方法

  1. public HashMap() {
  2. // 加载因子 = 默认加载因子 = 0.75f
  3. this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
  4. }
  5. // 给定一个默认容量
  6. public HashMap(int initialCapacity) {
  7. this(initialCapacity, DEFAULT_LOAD_FACTOR);
  8. }
  9. // 自定义 容量 以及 加载因子
  10. public HashMap(int initialCapacity, float loadFactor) {
  11. if (initialCapacity < 0)
  12. throw new IllegalArgumentException("Illegal initial capacity: " +
  13. initialCapacity);
  14. if (initialCapacity > MAXIMUM_CAPACITY)
  15. initialCapacity = MAXIMUM_CAPACITY;
  16. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  17. throw new IllegalArgumentException("Illegal load factor: " +
  18. loadFactor);
  19. this.loadFactor = loadFactor;
  20. // 注意:tableSizeFor 一个必问必知的方法,在下面核心方法中讲解。
  21. // 返回给定容量的最大2次幂;例如 initialCapacity = 6 返回 8(2的3次方);initialCapacity = 12 返回 16(2的4次方)
  22. this.threshold = tableSizeFor(initialCapacity);
  23. }
  24. public HashMap(Map<? extends K, ? extends V> m) {
  25. this.loadFactor = DEFAULT_LOAD_FACTOR;
  26. // 见下
  27. putMapEntries(m, false);
  28. }
  29. final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
  30. // 获取元素个数
  31. int s = m.size();
  32. if (s > 0) {
  33. // 如果 hashmap 还未初始化
  34. if (table == null) { // pre-size
  35. // +1 是为了除不尽的情况
  36. float ft = ((float)s / loadFactor) + 1.0F;
  37. int t = ((ft < (float)MAXIMUM_CAPACITY) ?
  38. (int)ft : MAXIMUM_CAPACITY);
  39. if (t > threshold)
  40. // 计算扩容阈值
  41. threshold = tableSizeFor(t);
  42. }
  43. else if (s > threshold)
  44. resize();
  45. for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
  46. K key = e.getKey();
  47. V value = e.getValue();
  48. putVal(hash(key), key, value, false, evict);
  49. }
  50. }
  51. }

常见方法

核心方法hash、resize、tableSizeFor等

  1. // hash算法
  2. static final int hash(Object key) {
  3. int h;
  4. // ^:异或运算; >>>:无符号右移
  5. // 1^1=0; 1^0=1; 0^1=1; 0^0=0
  6. // 这里又是异或又是位运算,理由呢。可以参照这个大神的解答:https://www.zhihu.com/question/20733617
  7. // 简单来说:这段代码叫“扰动函数”,目的就是为了混合原始hash的高位与低位,以此来增加低位的随机性;减少hash冲突
  8. return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  9. }
  10. // ***** 计算扩容因子 *****
  11. // >>>:无符号右移,a>>>b a向右移动b位,高位用0补,低位舍弃
  12. // |:或运算 有1必为1。 |= ---> a=a|b
  13. // 假设入参为6 6-1=5 转换成2进制即为 0101
  14. // 0101 >>> 1 ---> 0010
  15. // 0101 | 0010 ---> 0111
  16. // 0111 >>> 2 ---> 000111
  17. // 0111 | 000111 ---> 000111
  18. // 同理...
  19. // 最后 n+1 = 0111 + 1 = 1000 (8)
  20. static final int tableSizeFor(int cap) {
  21. // 这里-1就是为了防止 cap 本来就是2次幂。
  22. int n = cap - 1;
  23. n |= n >>> 1;
  24. n |= n >>> 2;
  25. n |= n >>> 4;
  26. n |= n >>> 8;
  27. n |= n >>> 16;
  28. return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
  29. }
  30. // ***** 扩容 *****
  31. final Node<K,V>[] resize() {
  32. // 底层数组
  33. Node<K,V>[] oldTab = table;
  34. // 底层数组的长度(不是元素数量)
  35. int oldCap = (oldTab == null) ? 0 : oldTab.length;
  36. // 扩容因子 threshold = capacity*loadFactor
  37. int oldThr = threshold;
  38. int newCap, newThr = 0;
  39. // 如果长度不为0 也就是说 不是首次加载 hashmap在put时添加数据(懒加载)
  40. if (oldCap > 0) {
  41. // 如果数组长度大于等于最大容量
  42. if (oldCap >= MAXIMUM_CAPACITY) {
  43. threshold = Integer.MAX_VALUE;
  44. return oldTab;
  45. }
  46. // 如果 原长度*2 < 最大容量 且 原长度 >= 16 那么就扩容两倍且把扩容因子*2 注意点1
  47. // newCap = oldCap * 2 新长度为旧长度的2倍 ***********
  48. else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
  49. // DEFAULT_INITIAL_CAPACITY = 1<<4 = 16
  50. oldCap >= DEFAULT_INITIAL_CAPACITY)
  51. // 扩容因子 * 2
  52. newThr = oldThr << 1;
  53. }
  54. // 如果oldCap<=0,且oldThr>0 说明已经初始化了,例如把元素删除完之后的情况,那么它的临界值肯定还存在;如果是首次初始化,它的临界值则为0
  55. // 如果不是首次初始化
  56. else if (oldThr > 0)
  57. // 这种情况下,数组容量=old扩容阈值
  58. newCap = oldThr;
  59. else { // 如果是首次初始化
  60. newCap = DEFAULT_INITIAL_CAPACITY;
  61. newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
  62. }
  63. // 这里是对 注意点1 的完善,如果注意点1的条件不满足 那么newThr还是未赋值的状态。
  64. if (newThr == 0) {
  65. float ft = (float)newCap * loadFactor;
  66. newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
  67. (int)ft : Integer.MAX_VALUE);
  68. }
  69. threshold = newThr;
  70. @SuppressWarnings({ "rawtypes","unchecked"})
  71. // 初始化
  72. Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
  73. table = newTab;
  74. if (oldTab != null) {
  75. // 遍历旧数组
  76. for (int j = 0; j < oldCap; ++j) {
  77. Node<K,V> e;
  78. if ((e = oldTab[j]) != null) {
  79. // 设为null 方便GC回收。
  80. oldTab[j] = null;
  81. if (e.next == null)
  82. // 下标计算 e.hash & (newCap - 1)
  83. // 如果下一个节点为null,重新计算新的节点位置 并把节点放入新的位置
  84. // newCap为2次幂 所以-1 低位全是1 所以位置信息由e.hash决定。
  85. newTab[e.hash & (newCap - 1)] = e;
  86. else if (e instanceof TreeNode) //红黑树操作
  87. ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
  88. else { // 链表 resize最重要的操作之一就是对链表的拆分;
  89. // 可以去看看其他博主写的文章:https://blog.csdn.net/weixin_41565013/article/details/93190786
  90. // 链表1
  91. Node<K,V> loHead = null, loTail = null;
  92. // 链表2
  93. Node<K,V> hiHead = null, hiTail = null;
  94. Node<K,V> next;
  95. // 这里注意:新的位置 = 【原位置】 或 【原位置 + 原数组长度】
  96. do {
  97. next = e.next;
  98. // oldCap 为 2次幂 低位全为0 高位为1
  99. // 所以 (e.hash & oldCap) 只能等于 1 或 0,这里把0,1分成了两个链表
  100. // 如果 等于 0
  101. if ((e.hash & oldCap) == 0) {
  102. // 如果尾节点为null
  103. if (loTail == null)
  104. loHead = e;
  105. else
  106. // 链表尾添加
  107. loTail.next = e;
  108. loTail = e;
  109. }
  110. else { // (e.hash & oldCap) = 1
  111. if (hiTail == null)
  112. hiHead = e;
  113. else
  114. hiTail.next = e;
  115. hiTail = e;
  116. }
  117. } while ((e = next) != null);
  118. if (loTail != null) {
  119. loTail.next = null;
  120. // 把链表头指向数组[j]
  121. newTab[j] = loHead;
  122. }
  123. if (hiTail != null) {
  124. hiTail.next = null;
  125. // 把链表头指向 newTab[原下标+原数组长度]
  126. newTab[j + oldCap] = hiHead;
  127. }
  128. }
  129. }
  130. }
  131. }
  132. return newTab;
  133. }

put

  1. public V put(K key, V value) {
  2. return putVal(hash(key), key, value, false, true);
  3. }
  4. // onlyIfAbsent:如果当前位置已存在一个值,是否替换,false是替换,true是不替换
  5. // evict:表是否在创建模式,如果为false,则表是在创建模式。
  6. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
  7. Node<K,V>[] tab; Node<K,V> p; int n, i;
  8. // 如果table未初始化或为空 则扩容
  9. if ((tab = table) == null || (n = tab.length) == 0)
  10. // 扩容方法 resize(),详情见上resize()解析
  11. n = (tab = resize()).length;
  12. if ((p = tab[i = (n - 1) & hash]) == null) // n-1 必为低位全为1
  13. // 如果这个下标下没有数据,那么就赋值。
  14. tab[i] = newNode(hash, key, value, null);
  15. else {
  16. // 如果该下标 已有节点
  17. Node<K,V> e; K k;
  18. // 这里分三种情况
  19. // 1. 如果hash 且 key相等,则直接替换旧值
  20. // 2. 如果是红黑树,则调用红黑树的put方法
  21. // 3. 如果是链表,使用尾插法。接着判断是否达到转树的条件,如果达到条件,那么就转成红黑树
  22. if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
  23. // 如果hash 相等 key也相等 那么就是同一个对象。
  24. e = p;
  25. else if (p instanceof TreeNode)
  26. // 如果是红黑树,则调用红黑树的方法。
  27. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  28. else { // 否则就是链表操作
  29. for (int binCount = 0; ; ++binCount) {
  30. // 如果没有下一个节点
  31. if ((e = p.next) == null) {
  32. // 创建节点 放到链表最后(尾插法)
  33. p.next = newNode(hash, key, value, null);
  34. //static final int TREEIFY_THRESHOLD =8; 当链表长度大于8则会转成红黑树
  35. if (binCount >= TREEIFY_THRESHOLD - 1)//-1 for 1st:-1是为了第一次循环
  36. // 转成红黑树结构
  37. treeifyBin(tab, hash);
  38. break;
  39. }
  40. if (e.hash == hash &&
  41. ((k = e.key) == key || (key != null && key.equals(k))))
  42. break;
  43. p = e;
  44. }
  45. }
  46. if (e != null) { // existing mapping for key
  47. V oldValue = e.value;
  48. if (!onlyIfAbsent || oldValue == null)
  49. e.value = value;
  50. afterNodeAccess(e);
  51. return oldValue;
  52. }
  53. }
  54. ++modCount;
  55. if (++size > threshold)
  56. resize();
  57. afterNodeInsertion(evict);
  58. return null;
  59. }
  60. // 转成树
  61. final void treeifyBin(Node<K,V>[] tab, int hash) {
  62. int n, index; Node<K,V> e;
  63. // 如果数组长度<64 就去扩容;如果长度大于>=64;转成红黑树
  64. if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
  65. resize();
  66. else if ((e = tab[index = (n - 1) & hash]) != null) { // 如果数组该下标下有元素
  67. // 定义首、尾节点
  68. TreeNode<K,V> hd = null, tl = null;
  69. // 把单向链表转成双向链表
  70. do {
  71. // 创建树节点
  72. TreeNode<K,V> p = replacementTreeNode(e, null);
  73. // 如果尾节点为空,说明还没有根节点
  74. if (tl == null)
  75. hd = p;
  76. else {
  77. p.prev = tl;
  78. tl.next = p;
  79. }
  80. tl = p;
  81. } while ((e = e.next) != null);
  82. // 把原来的单向链表换成双向链表
  83. if ((tab[index] = hd) != null)
  84. hd.treeify(tab);
  85. }
  86. }
  87. TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
  88. return new TreeNode<>(p.hash, p.key, p.value, next);
  89. }
  90. final void treeify(Node<K,V>[] tab) {
  91. TreeNode<K,V> root = null;
  92. for (TreeNode<K,V> x = this, next; x != null; x = next) {
  93. // 初始化
  94. next = (TreeNode<K,V>)x.next;
  95. x.left = x.right = null;
  96. // 如果根节点为null
  97. if (root == null) {
  98. x.parent = null; // 父节点为null
  99. x.red = false; // 置为黑色
  100. root = x; // 则把x(当前节点)设为根节点
  101. } else { // 如果已存在根节点
  102. K k = x.key; // 当前节点的key
  103. int h = x.hash; // 当前节点的hash
  104. Class<?> kc = null; // key的class
  105. for (TreeNode<K,V> p = root;;) {
  106. int dir, ph;
  107. K pk = p.key;
  108. // 如果p(根节点)的hash值 > 当前节点的hash值
  109. if ((ph = p.hash) > h)
  110. // 放到左侧的标记
  111. dir = -1;
  112. else if (ph < h)
  113. // 否则放到右侧
  114. dir = 1;
  115. // 如果hash值还是相等,那么就需要其他方法进行比较
  116. // 如果当前链表节点的key实现了comparable接口,并且当前树节点和链表节点是相同Class的实例,那么通过comparable的方式再比较两者
  117. else if ((kc == null &&
  118. (kc = comparableClassFor(k)) == null) ||
  119. (dir = compareComparables(kc, k, pk)) == 0)
  120. dir = tieBreakOrder(k, pk);
  121. /* * 如果dir小于等于0 :当前链表节点一定放置在当前树节点的左侧,但不一定是该树节点的左孩子,也可能是左孩子的右孩子 或者 更深层次的节点。 * 如果dir大于0 :当前链表节点一定放置在当前树节点的右侧,但不一定是该树节点的右孩子,也可能是右孩子的左孩子 或者 更深层次的节点。 * 如果当前树节点不是叶子节点,那么最终会以当前树节点的左孩子或者右孩子 为 起始节点 再进行下一个循环 重新寻找自己(当前链表节点)的位置 * 如果当前树节点就是叶子节点,那么根据dir的值,就可以把当前链表节点挂载到当前树节点的左或者右侧了。 * 挂载之后,还需要重新把树进行平衡。平衡之后,就可以针对下一个链表节点进行处理了。 */
  122. TreeNode<K,V> xp = p;
  123. if ((p = (dir <= 0) ? p.left : p.right) == null) {
  124. x.parent = xp;
  125. if (dir <= 0)
  126. xp.left = x;
  127. else
  128. xp.right = x;
  129. root = balanceInsertion(root, x);
  130. break;
  131. }
  132. }
  133. }
  134. }
  135. moveRootToFront(tab, root);
  136. }

get

  1. public V get(Object key) {
  2. Node<K,V> e;
  3. // 核心是getNode方法
  4. return (e = getNode(hash(key), key)) == null ? null : e.value;
  5. }
  6. final Node<K,V> getNode(int hash, Object key) {
  7. Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
  8. if ((tab = table) != null // table不为null
  9. && (n = tab.length) > 0 // 长度>0
  10. && (first = tab[(n - 1) & hash]) != null) { // 当前桶位有值的话
  11. if (first.hash == hash && // always check first node 如果是第一个节点,直接返回。
  12. ((k = first.key) == key || (key != null && key.equals(k))))
  13. return first;
  14. // 如果第一个节点的next节点不为null
  15. if ((e = first.next) != null) {
  16. // 如果是红黑树结构
  17. if (first instanceof TreeNode)
  18. // 使用树的方法
  19. return ((TreeNode<K,V>)first).getTreeNode(hash, key);
  20. // while 循环,如果key已经相等,就说明找到了;就返回。
  21. do {
  22. if (e.hash == hash &&
  23. ((k = e.key) == key || (key != null && key.equals(k))))
  24. return e;
  25. } while ((e = e.next) != null);
  26. }
  27. }
  28. return null;
  29. }
  30. final TreeNode<K,V> getTreeNode(int h, Object k) {
  31. // 获取根节点调用 find() 方法
  32. return ((parent != null) ? root() : this).find(h, k, null);
  33. }
  34. // 获取红黑树的根节点
  35. final TreeNode<K,V> root() {
  36. for (TreeNode<K,V> r = this, p;;) {
  37. // 如果没有父节点,那么当前节点就是根节点。
  38. if ((p = r.parent) == null)
  39. return r;
  40. r = p;
  41. }
  42. }
  43. // 红黑树的查询算法
  44. // kc:k的Class对象,该Class应该是实现了Comparable<K>的,否则应该是null
  45. final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
  46. TreeNode<K,V> p = this;
  47. do {
  48. int ph, dir; K pk;
  49. // pl:左子树节点;pr:右子树节点
  50. TreeNode<K,V> pl = p.left, pr = p.right, q;
  51. // 比较根节点的hash跟 需要查询的节点的hash值
  52. // 如果根节点的hash比需查询节点的hash值大,那么就往左子树查询。
  53. if ((ph = p.hash) > h)
  54. p = pl;
  55. else if (ph < h)
  56. p = pr;
  57. else if ((pk = p.key) == k || (k != null && k.equals(pk))) // 如果hash相等且key相等,直接放回当前节点即可。
  58. return p;
  59. else if (pl == null) // 如果左子树为null,那么转到右子树
  60. p = pr;
  61. else if (pr == null) // 如果右子树为null,那么转到左子树
  62. p = pl;
  63. else if ((kc != null ||
  64. (kc = comparableClassFor(k)) != null) &&
  65. (dir = compareComparables(kc, k, pk)) != 0)
  66. p = (dir < 0) ? pl : pr; //dir小于0,p指向右孩子,否则指向右孩子。紧接着就是下一轮循环了
  67. // 执行到这里说明无法通过comparable比较 或者 比较之后还是相等
  68. // 从右孩子节点递归循环查找,如果找到了匹配的则返回
  69. else if ((q = pr.find(h, k, kc)) != null)
  70. return q;
  71. else
  72. p = pl;
  73. } while (p != null);
  74. return null;
  75. }

remove

  1. public V remove(Object key) {
  2. Node<K,V> e;
  3. return (e = removeNode(hash(key), key, null, false, true)) == null ?
  4. null : e.value;
  5. }
  6. /** * Implements Map.remove and related methods. * * @param hash: hash for key (key的hash) * @param key: the key * @param value: the value to match if matchValue, else ignored 如果是matchValue,则匹配值,否则忽略 * @param matchValue: if true only remove if value is equal 如果为true,只在value相等时移除 * @param movable: if false do not move other nodes while removing 如果为false,则在移除时不要移动其他节点 * @return the node, or null if none */
  7. final Node<K,V> removeNode(int hash, Object key, Object value,
  8. boolean matchValue, boolean movable) {
  9. Node<K,V>[] tab; Node<K,V> p; int n, index;
  10. // 如果数组不为null 长度大于0 且根据key得到的数组下标中有值。
  11. if ((tab = table) != null && (n = tab.length) > 0 &&
  12. (p = tab[index = (n - 1) & hash]) != null) {
  13. Node<K,V> node = null, e; K k; V v;
  14. // 如果key相等
  15. if (p.hash == hash &&
  16. ((k = p.key) == key || (key != null && key.equals(k))))
  17. node = p;
  18. else if ((e = p.next) != null) { // 如果p.next 不为null
  19. if (p instanceof TreeNode) // 如果为红黑树,则使用树的方法
  20. node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
  21. else {
  22. // 不是红黑树 就是链表,循环链表。
  23. do {
  24. if (e.hash == hash && ((k = e.key) == key ||
  25. (key != null && key.equals(k)))) {
  26. // 如果key一致,则跳出循环,并拿到node节点。
  27. node = e;
  28. break;
  29. }
  30. p = e;
  31. } while ((e = e.next) != null);
  32. }
  33. }
  34. if (node != null && (!matchValue || (v = node.value) == value ||
  35. (value != null && value.equals(v)))) {
  36. if (node instanceof TreeNode)
  37. ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
  38. else if (node == p) // 如果链表头==需要删除的节点
  39. // 那么直接让node节点的next节点指向数组当前位置
  40. tab[index] = node.next;
  41. else
  42. p.next = node.next;
  43. ++modCount;
  44. --size;
  45. afterNodeRemoval(node);
  46. return node;
  47. }
  48. }
  49. return null;
  50. }

总结

HashMap中最重要的几点:

  • 底层结构 数组+链表+红黑树
  • 几个关键的数字:16(默认容量) 0.75f(默认扩容因子) 12(16*0.75 默认扩容阈值) 6(当链表长度小于6时,转成链表) 8(当链表长度大于8时,转成红黑树) 64(当底层数组容量大于64时,转成红黑树)
  • 扩容方法resize()
  • 扩容阈值计算方法
  • put方法
  • remove方法
  • 对于红黑树的理解(加分)

发表评论

表情:
评论列表 (有 0 条评论,599人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JDK8HashMap

    > 进入这篇文章之前,我想清楚的说一说怎么去理解`HashMap`源码。它先是使用的`hash`算法,那么哈希算法需要注意的那就是怎么`hash`,怎么减少冲突,怎么避免冲突。

    相关 HashMap解析JDK1.8

     今天,打算写一篇HashMap的源码解析,主要是针对增删改查操作,废话不多说,直接开始。    先看看hashMap在jdk 1.8的结构,如下图,用的是数组+链表+红