Java 1.8 HashMap源码解析 桶数组+单链表+红黑树

1 // 非线程安全
2 // 继承了AbstractMap
3 // 实现了Map、Cloneable、Serializable接口
4 // 后面2个接口是标记接口,没有抽象方法。
5 // 表示HashMap可以浅复制、序列化和反序列化。
6 public class HashMap<K,V> extends AbstractMap<K,V>
7     implements Map<K,V>, Cloneable, Serializable
1 // 序列化版本唯一标识符,版本向上兼容。
2 private static final long serialVersionUID = 362498820763181265L;
1 // 默认初始容量为16。
2 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
1 // 最大容量为2的30次方。
2 static final int MAXIMUM_CAPACITY = 1 << 30;
1 // 默认的负载因子为0.75。 容量*负载因子等于最大的键值对数量即键值对数量阈值。
2 static final float DEFAULT_LOAD_FACTOR = 0.75f;
1 // 最大的单链表长度-1,单链表结点数等于它时需要把该链表转换成红黑树。
2 static final int TREEIFY_THRESHOLD = 8;
1 // 当某个桶中数量减少到6时,需要把对应的红黑树转为单链表。
2 static final int UNTREEIFY_THRESHOLD = 6;
1 // 当桶数量达到64个的时候,将所有的单链表转为红黑树。
2 static final int MIN_TREEIFY_CAPACITY = 64;
 1 // 结点类
 2 // 实现了Map.Entry接口
 3 static class Node<K,V> implements Map.Entry<K,V> {
 4     // 属性
 5     final int hash;
 6     final K key;
 7     V value;
 8     Node<K,V> next;
 9
10     // 构造方法
11     Node(int hash, K key, V value, Node<K,V> next) {
12         this.hash = hash;
13         this.key = key;
14         this.value = value;
15         this.next = next;
16     }
17
18     // get方法和toString方法
19     public final K getKey()        { return key; }
20     public final V getValue()      { return value; }
21     public final String toString() { return key + "=" + value; }
22
23     // key值和value值对应的hashCode值进行异或运算后得到当前结点的hashCode值
24     public final int hashCode() {
25         return Objects.hashCode(key) ^ Objects.hashCode(value);
26     }
27
28     // 设置value值,并返回旧值。
29     public final V setValue(V newValue) {
30         V oldValue = value;
31         value = newValue;
32
33         return oldValue;
34     }
35
36     // 比较内容
37     public final boolean equals(Object o) {
38         // 判断是否为同一个对象
39         if (o == this)
40             return true;
41
42         // Map.Entry实例泛型不一定是<K,V>,强制转换可能会出错。
43         if (o instanceof Map.Entry) {
44             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
45             // 用equals方法来比较内容。
46             if (Objects.equals(key, e.getKey()) &&
47                 Objects.equals(value, e.getValue()))
48                 return true;
49         }
50
51         return false;
52     }
53 }
1 // 根据key得到hash值。
2 static final int hash(Object key) {
3     int h;
4     // 如果key为空则hash值为0,否则将key的hashCode值与它右移16位的结果进行异或运算作为hash值。
5     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
6 }
 1 // 返回大于输入参数且最近的2的整数次幂的数。
 2 static final int tableSizeFor(int cap) {
 3     int n = cap - 1;
 4     n |= n >>> 1;
 5     n |= n >>> 2;
 6     n |= n >>> 4;
 7     n |= n >>> 8;
 8     n |= n >>> 16;
 9     return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
10 }
1 // 桶数组
2 transient Node<K,V>[] table;
1 // 键值对数量
2 transient int size;
1 // 快速失败机制
2 transient int modCount;
1 // 键值对数量的阈值。
2 int threshold;
1 // 负载因子
2 final float loadFactor;
 1 // 根据初始容量和负载因子来初始化哈希映射表。
 2 public HashMap(int initialCapacity, float loadFactor) {
 3     // 初始容量不能为负数。
 4     if (initialCapacity < 0)
 5         throw new IllegalArgumentException("Illegal initial capacity: " +
 6                                            initialCapacity);
 7
 8     // 确保容量最大为MAXIMUM_CAPACITY。
 9     if (initialCapacity > MAXIMUM_CAPACITY)
10         initialCapacity = MAXIMUM_CAPACITY;
11
12     // 判断负载因子是否合法。
13     if (loadFactor <= 0 || Float.isNaN(loadFactor))
14         throw new IllegalArgumentException("Illegal load factor: " +
15                                            loadFactor);
16
17     // 初始化负载因子和键值对数量阈值。
18     this.loadFactor = loadFactor;
19     this.threshold = tableSizeFor(initialCapacity);
20 }
1 // 根据初始容量来初始化哈希映射表。
2 public HashMap(int initialCapacity) {
3     // 根据初始容量和负载因子来初始化哈希映射表。
4     this(initialCapacity, DEFAULT_LOAD_FACTOR);
5 }
1 // 根据默认值来初始化哈希映射表。
2 public HashMap() {
3     this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
4 }
1 // 构造一个映射关系与指定Map相同的新 HashMap。
2 public HashMap(Map<? extends K, ? extends V> m) {
3     // 使用默认的负载因子。
4     this.loadFactor = DEFAULT_LOAD_FACTOR;
5     // 插入m中的键值对。
6     putMapEntries(m, false);
7 }
 1 // 插入键值对,用于clone()方法、构造方法。
 2 // evict为false表示初始化构造该map后插入键值对。
 3 // evict为true表示插入结点之后插入键值对。
 4 final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
 5     // 获取键值对数量
 6     int s = m.size();
 7     // 如果存在键值对
 8     if (s > 0) {
 9         // 如果桶数组为空
10         if (table == null) { // pre-size
11             // 计算之前的容量
12             float ft = ((float)s / loadFactor) + 1.0F;
13             // 更新容量
14             int t = ((ft < (float)MAXIMUM_CAPACITY) ?
15                      (int)ft : MAXIMUM_CAPACITY);
16             // 更新阈值
17             if (t > threshold)
18                 threshold = tableSizeFor(t);
19         }
20         else if (s > threshold) // 如果键值对数量超过键值对数量阈值
21             resize(); // 扩容
22
23         // 键值对依次插入
24         for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
25             K key = e.getKey();
26             V value = e.getValue();
27
28             // 插入键值对
29             putVal(hash(key), key, value, false, evict);
30         }
31     }
32 }
1 // 获取键值对数量。
2 public int size() {
3     return size;
4 }
1 // 判断是否为空即判断是否存在键值对。
2 public boolean isEmpty() {
3     return size == 0;
4 }
1 // 根据key获取value。
2 public V get(Object key) {
3     Node<K,V> e;
4     // 根据key查询
5     return (e = getNode(hash(key), key)) == null ? null : e.value;
6 }
 1 // 根据hash值和key得到相应的结点。
 2 final Node<K,V> getNode(int hash, Object key) {
 3     Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
 4     // 如果桶数组不为空并且对应的元素指向的单链表或者红黑树不为空
 5     if ((tab = table) != null && (n = tab.length) > 0 &&
 6         (first = tab[(n - 1) & hash]) != null) {
 7         if (first.hash == hash && // always check first node
 8             ((k = first.key) == key || (key != null && key.equals(k))))
 9             return first;
10
11         if ((e = first.next) != null) { // 如果存在第2个结点
12             if (first instanceof TreeNode) // 在红黑树中查找
13                 return ((TreeNode<K,V>)first).getTreeNode(hash, key);
14
15             do { // 在单链表中查找
16                 if (e.hash == hash &&
17                     ((k = e.key) == key || (key != null && key.equals(k))))
18                     return e;
19             } while ((e = e.next) != null);
20         }
21     }
22
23     // 没有找到key。
24     return null;
25 }
1 // 判断是否存在key。
2 public boolean containsKey(Object key) {
3     return getNode(hash(key), key) != null;
4 }
1 // 插入键值对。
2 public V put(K key, V value) {
3     return putVal(hash(key), key, value, false, true);
4 }
 1 // 插入键值对
 2 // onlyIfAbsent false 表示可以修改value
 3 // onlyIfAbsent true 表示不可以修改value
 4 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 5                boolean evict) {
 6     Node<K,V>[] tab; Node<K,V> p; int n, i;
 7
 8     // 如果桶数组为空
 9     if ((tab = table) == null || (n = tab.length) == 0)
10         n = (tab = resize()).length; // 扩容
11
12     // 如果桶数组元素为空则直接创建新结点。
13     if ((p = tab[i = (n - 1) & hash]) == null)
14         // 尾插入 第4个参数表示next
15         tab[i] = newNode(hash, key, value, null);
16     else {
17         Node<K,V> e; K k;
18         // 同一个桶中的结点hash值不一定相同。
19         // 判断第一个结点的key是否符合要求
20         // 如果hash值不同则equals方法一定返回false。
21         if (p.hash == hash &&
22             ((k = p.key) == key || (key != null && key.equals(k))))
23             e = p;
24         else if (p instanceof TreeNode) // 在红黑树中查找
25             e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
26         else { // 在单链表中查找
27             // binCount表示非空结点数量
28             for (int binCount = 0; ; ++binCount) {
29                 if ((e = p.next) == null) { // 尾插入 第4个参数表示next
30                     p.next = newNode(hash, key, value, null);
31
32                     // 如果单链表长度达到树形化阈值则将单链表转化为红黑树。
33                     if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
34                         treeifyBin(tab, hash);
35
36                     break;
37                 }
38
39                 // 如果存在相同的key则不操作结束。
40                 if (e.hash == hash &&
41                     ((k = e.key) == key || (key != null && key.equals(k))))
42                     break;
43
44                 // 遍历下一个结点。
45                 p = e;
46             }
47         }
48
49         if (e != null) { // existing mapping for key
50             V oldValue = e.value;
51             // 如果可以改变value或者旧值为空
52             if (!onlyIfAbsent || oldValue == null)
53                 e.value = value; // 改变value
54             afterNodeAccess(e);
55
56             return oldValue; // 返回旧的value
57         }
58     }
59
60     // 快速失败机制
61     ++modCount;
62     // 如果键值对数量超过键值对数量阈值则扩容。
63     if (++size > threshold)
64         resize();
65     // 插入后调整。
66     afterNodeInsertion(evict);
67
68     // 如果不存在相同的key则返回null作为原来的value。
69     return null;
70 }
 1 // 扩容
 2 final Node<K,V>[] resize() {
 3     Node<K,V>[] oldTab = table;
 4     int oldCap = (oldTab == null) ? 0 : oldTab.length;
 5     int oldThr = threshold;
 6     int newCap, newThr = 0;
 7
 8     // 根据初始化情况设置容量和阈值。
 9     // 如果初始化时设置了容量
10     if (oldCap > 0) {
11         if (oldCap >= MAXIMUM_CAPACITY) {
12             threshold = Integer.MAX_VALUE;
13             return oldTab;
14         }
15         else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
16                  oldCap >= DEFAULT_INITIAL_CAPACITY)
17             newThr = oldThr << 1; // double threshold
18     }
19     // 初始化时没有设置过容量默认为0则用阈值作为容量。
20     else if (oldThr > 0) // initial capacity was placed in threshold
21         newCap = oldThr;
22     // 初始化时只设置了负载因子则用默认值设置容量和阈值。
23     else {               // zero initial threshold signifies using defaults
24         newCap = DEFAULT_INITIAL_CAPACITY;
25         newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
26     }
27
28     // 如果新阈值为0
29     if (newThr == 0) {
30         float ft = (float)newCap * loadFactor;
31         newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
32                   (int)ft : Integer.MAX_VALUE);
33     }
34     threshold = newThr; // 更新阈值
35
36     @SuppressWarnings({"rawtypes","unchecked"})
37     // 创建新的桶数组。
38     Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
39     table = newTab;
40     if (oldTab != null) {
41         for (int j = 0; j < oldCap; ++j) {
42             Node<K,V> e;
43             if ((e = oldTab[j]) != null) {
44                 oldTab[j] = null;
45                 // 如果是空链表
46                 if (e.next == null)
47                     newTab[e.hash & (newCap - 1)] = e;
48                 // 如果是红黑树
49                 else if (e instanceof TreeNode)
50                     ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
51                 else { // preserve order
52                     Node<K,V> loHead = null, loTail = null;
53                     Node<K,V> hiHead = null, hiTail = null;
54                     Node<K,V> next;
55
56                     // 头插法,保证与原来的次序相同
57                     do {
58                         next = e.next;
59                         // 索引在原来的位置
60                         // 通过原来容量的最高位1与hash值与运算如果是1表示索引需要增量
61                         // 否则表示索引保持不变
62                         // 例如原来索引是9,原来容量是16,则之后的索引只能是9或25
63                         if ((e.hash & oldCap) == 0) {
64                             if (loTail == null)
65                                 loHead = e;
66                             else
67                                 loTail.next = e;
68                             loTail = e;
69                         }
70                         // 索引在原来增量的位置
71                         else {
72                             if (hiTail == null)
73                                 hiHead = e;
74                             else
75                                 hiTail.next = e;
76                             hiTail = e;
77                         }
78                     } while ((e = next) != null);
79
80                     // 将链表挂到数组上
81                     if (loTail != null) {
82                         loTail.next = null;
83                         newTab[j] = loHead;
84                     }
85                     if (hiTail != null) {
86                         hiTail.next = null;
87                         newTab[j + oldCap] = hiHead;
88                     }
89                 }
90             }
91         }
92     }
93
94     return newTab; // 返回新的桶数组。
95 }
 1 // 根据hash将桶内的结点树形化。
 2 final void treeifyBin(Node<K,V>[] tab, int hash) {
 3     int n, index; Node<K,V> e;
 4     // 如果桶数组为空或者桶数组长度还没有达到最小树形化容量则需要扩容。
 5     if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
 6         resize();
 7     // 如果hash值对应的单链表不为空
 8     else if ((e = tab[index = (n - 1) & hash]) != null) {
 9         TreeNode<K,V> hd = null, tl = null;
10
11         // 遍历单链表,将结点作为红黑树结点重新构造单链表。
12         do {
13             // 将单链表结点替换成红黑树结点。第2个参数表示孩子结点。
14             TreeNode<K,V> p = replacementTreeNode(e, null);
15             if (tl == null)
16                 hd = p;
17             else {
18                 p.prev = tl; // 在单链表中,tl是p的前驱结点。
19                 tl.next = p; // 在单链表中,p是tl的后继结点。
20             }
21             tl = p; // 在单链表中,tl是下一个结点的前驱结点。
22         } while ((e = e.next) != null);
23
24         // 桶数组元素指向红黑树的单链表 如果单链表非空则把该单链表转成红黑树。
25         if ((tab[index] = hd) != null)
26             hd.treeify(tab);
27     }
28 }
1 // 将指定映射的所有映射关系复制到此映射中,这些映射关系将替换此映射目前针对指定映射中所有键的所有映射关系。
2 public void putAll(Map<? extends K, ? extends V> m) {
3     putMapEntries(m, true);
4 }
1 // 删除key
2 public V remove(Object key) {
3     Node<K,V> e;
4     return (e = removeNode(hash(key), key, null, false, true)) == null ?
5         null : e.value;
6 }
 1 // 删除结点并返回删除的结点。
 2 final Node<K,V> removeNode(int hash, Object key, Object value,
 3                            boolean matchValue, boolean movable) {
 4     Node<K,V>[] tab; Node<K,V> p; int n, index;
 5
 6     // 如果table非空并且table数组长度大于0并且hash值对应的单链表不为空
 7     if ((tab = table) != null && (n = tab.length) > 0 &&
 8         (p = tab[index = (n - 1) & hash]) != null) {
 9         // node是目标结点
10         Node<K,V> node = null, e; K k; V v;
11
12         // 如果首结点就是目标结点
13         if (p.hash == hash &&
14             ((k = p.key) == key || (key != null && key.equals(k))))
15             node = p;
16         // 如果首结点存在后继结点
17         else if ((e = p.next) != null) { // p是e的前驱结点
18             // 如果当前结点是红黑树的结点
19             if (p instanceof TreeNode)
20                 node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
21             // 否则
22             else {
23                 // 遍历单链表寻找目标结点
24                 do {
25                     // 如果当前结点是目标结点
26                     if (e.hash == hash &&
27                         ((k = e.key) == key ||
28                          (key != null && key.equals(k)))) {
29                         node = e;
30                         break; // 跳出循环
31                     }
32                     p = e; // p是e的前驱结点。
33                 } while ((e = e.next) != null);
34             }
35         }
36
37         // 如果找到目标结点
38         if (node != null && (!matchValue || (v = node.value) == value ||
39                              (value != null && value.equals(v)))) {
40             // 如果目标结点是红黑树的结点
41             if (node instanceof TreeNode)
42                 ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
43             // 如果目标结点是单链表的首结点
44             else if (node == p)
45                 tab[index] = node.next;
46             // 前驱结点的后继结点是目标结点的后继结点。
47             else
48                 p.next = node.next;
49
50             ++modCount; // 快速失败机制
51             --size; // 键值对数量减少。
52             afterNodeRemoval(node);
53
54             return node; // 返回删除的结点。
55         }
56     }
57
58     // 没有找到目标结点。
59     return null;
60 }
 1 // 清空哈希映射表。
 2 public void clear() {
 3     Node<K,V>[] tab;
 4     modCount++;
 5     // 如果桶数组不为空
 6     if ((tab = table) != null && size > 0) {
 7         size = 0; // 键值对数量设为0。
 8         for (int i = 0; i < tab.length; ++i)
 9             tab[i] = null; // let GC to do its work
10     }
11 }
 1 // 判断是否存在某个值value。
 2 public boolean containsValue(Object value) {
 3     Node<K,V>[] tab; V v;
 4     // 如果桶数组不为空并且存在键值对
 5     if ((tab = table) != null && size > 0) {
 6         // 遍历每个桶。
 7         for (int i = 0; i < tab.length; ++i) {
 8             // 遍历桶中的所有结点。
 9             for (Node<K,V> e = tab[i]; e != null; e = e.next) {
10                 // 如果存在value
11                 if ((v = e.value) == value ||
12                     (value != null && value.equals(v)))
13                     return true;
14             }
15         }
16     }
17
18     // 没有找到value。
19     return false;
20 }
1 // 删除一个键值对。
2 public boolean remove(Object key, Object value) {
3     return removeNode(hash(key), key, value, true, true) != null;
4 }
 1 // 根据键值对替换原来的value。
 2 public boolean replace(K key, V oldValue, V newValue) {
 3     Node<K,V> e; V v;
 4     if ((e = getNode(hash(key), key)) != null &&
 5         ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
 6         e.value = newValue;
 7         afterNodeAccess(e);
 8
 9         return true; // 替换成功
10     }
11
12     // 替换失败
13     return false;
14 }
 1 // 根据键替换原来的value。
 2 public V replace(K key, V value) {
 3     Node<K,V> e;
 4     if ((e = getNode(hash(key), key)) != null) {
 5         V oldValue = e.value;
 6         e.value = value;
 7         afterNodeAccess(e);
 8
 9         return oldValue; // 返回原来的value。
10     }
11
12     return null; // 没有找到则返回空。
13 }
 1 // 浅复制,没有复制桶数组里面存储的对象,返回HashMap对象。
 2 public Object clone() {
 3     HashMap<K,V> result;
 4     try {
 5         // 调用父类的克隆方法。
 6         result = (HashMap<K,V>)super.clone();
 7     } catch (CloneNotSupportedException e) {
 8         // this shouldn‘t happen, since we are Cloneable
 9         throw new InternalError(e);
10     }
11
12     // 重新初始化。
13     result.reinitialize();
14     // 在初始化后插入键值对。
15     result.putMapEntries(this, false);
16
17     return result; // 返回HashMap对象。
18 }
1 // 得到负载因子。
2 final float loadFactor() { return loadFactor; }
1 // 把容量设为哈希桶数量,也就是散列表长度。
2 final int capacity() {
3     return (table != null) ? table.length :
4         (threshold > 0) ? threshold :
5         DEFAULT_INITIAL_CAPACITY;
6 }
 1 // 序列化
 2 private void writeObject(java.io.ObjectOutputStream s)
 3     throws IOException {
 4     int buckets = capacity();
 5     // Write out the threshold, loadfactor, and any hidden stuff
 6     s.defaultWriteObject();
 7
 8     s.writeInt(buckets);
 9     s.writeInt(size);
10     // 把所有键值对写入输出流。
11     internalWriteEntries(s);
12 }
 1 // 反序列化
 2 private void readObject(java.io.ObjectInputStream s)
 3     throws IOException, ClassNotFoundException {
 4     // Read in the threshold (ignored), loadfactor, and any hidden stuff
 5     s.defaultReadObject();
 6     // 重新初始化哈希映射表。
 7     reinitialize();
 8     if (loadFactor <= 0 || Float.isNaN(loadFactor))
 9         throw new InvalidObjectException("Illegal load factor: " +
10                                          loadFactor);
11     s.readInt();                // Read and ignore number of buckets
12     int mappings = s.readInt(); // Read number of mappings (size)
13     if (mappings < 0)
14         throw new InvalidObjectException("Illegal mappings count: " +
15                                          mappings);
16     else if (mappings > 0) { // (if zero, use defaults)
17         // Size the table using given load factor only if within
18         // range of 0.25...4.0
19         float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
20         float fc = (float)mappings / lf + 1.0f;
21         int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
22                    DEFAULT_INITIAL_CAPACITY :
23                    (fc >= MAXIMUM_CAPACITY) ?
24                    MAXIMUM_CAPACITY :
25                    tableSizeFor((int)fc));
26         float ft = (float)cap * lf;
27         threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
28                      (int)ft : Integer.MAX_VALUE);
29         @SuppressWarnings({"rawtypes","unchecked"})
30         Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
31         table = tab;
32
33         // 从输入流中读取键值对并插入。
34         // Read the keys and values, and put the mappings in the HashMap
35         for (int i = 0; i < mappings; i++) {
36             @SuppressWarnings("unchecked")
37             K key = (K) s.readObject();
38             @SuppressWarnings("unchecked")
39             V value = (V) s.readObject();
40             // 插入键值对。
41             putVal(hash(key), key, value, false, false);
42         }
43     }
44 }
1 // Create a regular (non-tree) node
2 // 创建单链表结点。
3 Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
4     return new Node<>(hash, key, value, next);
5 }
1 // 创建红黑树结点对应的单链表结点。
2 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
3     return new Node<>(p.hash, p.key, p.value, next);
4 }
1 // 创建红黑树新结点。
2 TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
3     return new TreeNode<>(hash, key, value, next);
4 }
1 // 创建单链表结点对应的红黑树结点。
2 TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
3     return new TreeNode<>(p.hash, p.key, p.value, next);
4 }
 1 // 重新初始化哈希映射表。
 2 void reinitialize() {
 3     table = null;
 4     entrySet = null;
 5     keySet = null;
 6     values = null;
 7     modCount = 0;
 8     threshold = 0;
 9     size = 0;
10 }
 1 // 把所有键值对写入输出流。
 2 void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
 3     Node<K,V>[] tab;
 4     // 如果存在键值对并且桶数组不为空则把把所有键值对写入数据流
 5     if (size > 0 && (tab = table) != null) {
 6         // 遍历所有的桶。
 7         for (int i = 0; i < tab.length; ++i) {
 8             // 遍历所有的结点。
 9             for (Node<K,V> e = tab[i]; e != null; e = e.next) {
10                 s.writeObject(e.key);
11                 s.writeObject(e.value);
12             }
13         }
14     }
15 }
时间: 2024-08-08 13:55:15

Java 1.8 HashMap源码解析 桶数组+单链表+红黑树的相关文章

死磕 java集合之TreeMap源码分析(三)- 内含红黑树分析全过程

欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 删除元素 删除元素本身比较简单,就是采用二叉树的删除规则. (1)如果删除的位置有两个叶子节点,则从其右子树中取最小的元素放到删除的位置,然后把删除位置移到替代元素的位置,进入下一步. (2)如果删除的位置只有一个叶子节点(有可能是经过第一步转换后的删除位置),则把那个叶子节点作为替代元素,放到删除的位置,然后把这个叶子节点删除. (3)如果删除的位置没有叶子节点,则直接把这个删除位置的元素删除即可.

【转】Java HashMap 源码解析(好文章)

- .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wrapper iframe, .fluid-width-video-wrapper object, .fluid-width-video-wrapper embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } [

Java中的容器(集合)之HashMap源码解析

1.HashMap源码解析(JDK8) 基础原理: 对比上一篇<Java中的容器(集合)之ArrayList源码解析>而言,本篇只解析HashMap常用的核心方法的源码. HashMap是一个以键值对存储的容器. hashMap底层实现为数组+链表+红黑树(链表超过8时转为红黑树,JDK7为数组+链表). HashMap会根据key的hashCode得到对应的hash值,再去数组中找寻对应的数组位置(下标). hash方法如下: static final int hash(Object key

Java集合---Array类源码解析

Java集合---Array类源码解析              ---转自:牛奶.不加糖 一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型:采用改进的归并排序. 1.对于基本类型源码分析如下(以int[]为例): Java对Primitive(int,float等原型数据)数组采用快速排序,对Object对象数组采用归并排序.对这一区别,sun在

Java HashSet和HashMap源码剖析

转自: Java HashSet和HashMap源码剖析 总体介绍 之所以把HashSet和HashMap放在一起讲解,是因为二者在Java里有着相同的实现,前者仅仅是对后者做了一层包装,也就是说HashSet里面有一个HashMap(适配器模式).因此本文将重点分析HashMap. HashMap实现了Map接口,允许放入null元素,除该类未实现同步外,其余跟Hashtable大致相同,跟TreeMap不同,该容器不保证元素顺序,根据需要该容器可能会对元素重新哈希,元素的顺序也会被重新打散,

java.lang.Void类源码解析_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 在一次源码查看ThreadGroup的时候,看到一段代码,为以下: /* * @throws NullPointerException if the parent argument is {@code null} * @throws SecurityException if the current thread cannot create a * thread in the specified thread grou

【Java源码解析】-- HashMap源码解析

目录 源码解析 1.构造方法 无参构造方法 int型参数的构造方法 int,float两个参数的构造方法 hsah方法 2.添加元素(put()方法) 3.扩容方法(resize()方法) 4.获取元素(get()方法) 5.移除元素(remove()) 6.树化(treeifyBin()) 关于HashMap常见的问题 1.为什么容量始终是2的幂次? 3.既然红黑树那么好,为啥hashmap不直接采用红黑树,而是当大于等于8个的时候才转换红黑树? 4.JDK1.7 扩容死锁产生原因 5.JDK

【Java深入研究】9、HashMap源码解析(jdk 1.8)

一.HashMap概述 HashMap是常用的Java集合之一,是基于哈希表的Map接口的实现.与HashTable主要区别为不支持同步和允许null作为key和value.由于HashMap不是线程安全的,如果想要线程安全,可以使用ConcurrentHashMap代替. 二.HashMap数据结构 HashMap的底层是哈希数组,数组元素为Entry.HashMap通过key的hashCode来计算hash值,当hashCode相同时,通过"拉链法"解决冲突 相比于之前的版本,jd

HashMap 源码解析

HashMap简介: HashMap在日常的开发中应用的非常之广泛,它是基于Hash表,实现了Map接口,以键值对(key-value)形式进行数据存储,HashMap在数据结构上使用的是数组+链表.允许null键和null值,不保证键值对的顺序. HashMap检索数据的大致流程: 当我们使用HashMap搜索key所对应的value时,HashMap会根据Hash算法对key进行计算,得到一个key的hash值,再根据hash值算出该key在数组中存储的位置index,然后获取数组在inde