hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝
HashMap 的 put
和 get
操作是核心功能,其底层通过 数组+链表/红黑树 实现,结合哈希计算与冲突处理完成键值对的存取。以下是详细流程和关键逻辑分析:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
key.hashCode()
获取原始哈希值后,将高16位与低16位异或,减少哈希冲突。static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
作用:高位信息参与运算,避免低位重复导致大量哈希冲突。index = (n - 1) & hash
(n
为数组长度,必须是2的幂)。n
是2的幂时,(n-1) & hash
等效于 hash % n
,但位运算效率更高。场景1:桶为空
Node
)放入数组对应位置。场景2:桶非空(哈希冲突)
链表处理:
key
(hash
相同且 equals
为 true
),更新值并返回旧值。TREEIFY_THRESHOLD = 8
)。红黑树处理:
putTreeVal
方法插入节点,保持红黑树平衡。capacity * loadFactor
(默认 capacity=16
,loadFactor=0.75
)。hash & oldCap
判断高位是否为1)。public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
put
操作的扰动函数计算哈希值。(n-1) & hash
找到数组下标。hash
和 key
(equals
方法),找到匹配节点返回 value
。getTreeNode
方法,按红黑树特性快速查找(时间复杂度从链表 O(n) 降低到 O(logn))。UNTREEIFY_THRESHOLD = 6
,避免频繁转换)。hash & oldCap
判断高位,直接将链表拆分为低位和高位两部分。ConcurrentHashMap
或 Collections.synchronizedMap
。操作 | 步骤概要 | 时间复杂度(平均) | 时间复杂度(最坏) |
---|---|---|---|
put | 哈希计算 → 定位桶 → 插入/更新 → 扩容 | O(1) | O(logn)(红黑树) |
get | 哈希计算 → 定位桶 → 遍历链表/树 | O(1) | O(logn) |
为什么负载因子是0.75?
为什么链表长度≥8才转红黑树?
头插法改为尾插法的原因?
为什么数组长度是2的幂?
(n-1) & hash
均匀分布,且扩容时拆分链表更高效。