一.HashMap的原理
1.HashMap内部结构
HashMap底层就是一个数组,结合链表和红黑树来解决冲突。无论有没有哈希冲突,数组桶里永远只存“指向节点的引用(箭头)”,节点对象本身(包含 key、value、next)存在堆内存中。
// HashMap底层数组
Node<K,V>[] table;
// 假设桶下标 3 只有一个节点
Node<String,Integer> node = new Node<>("苹果", 5, null);
table[3] = node; // 桶里存这个节点的引用
System.out.println(node.next); // 输出 null
2.Java 中 HashMap 的链表节点(Node)
static class Node<K,V> {
final int hash;
final K key; // 自己的名字(当前节点的标识)
V value; // 自己的数据
Node<K,V> next; // 这就是“纸条”,写下一个人的名字
}其中next 就是“下一个节点对象的地址”(在 Java 里叫“对象引用”)。
3.当同一个数组桶里需要使用链表时,JDK 1.7采用头插法,JDK 1.8+采用尾插法。
(1)头插法(JDK 1.7)
原链表:table[3] → 苹果( next → 香蕉( next → null ) )
插入新节点“橙子”:
创建新节点
橙子,它的next指向当前头节点苹果。然后
table[3] = 橙子。结果链表:
橙子 → 苹果 → 香蕉 → null苹果的
next没有变,仍然指向香蕉。
所以“苹果”的
next不需要重新赋值。
(2)尾插法(JDK 1.8+)
原链表:table[3] → 苹果( next → 香蕉( next → null ) )
插入新节点“橙子”:
遍历到链表尾部(香蕉节点),发现
香蕉.next == null。创建新节点
橙子,橙子.next = null。将
香蕉.next = 橙子。结果链表:
苹果 → 香蕉 → 橙子 → null尾节点(香蕉)的
next被重新赋值为橙子。
如果原链表只有一个节点“苹果”(苹果.next == null),那么苹果的next会被重新赋值为橙子。