java基础-Java集合框架-Map接口-HashMap原理(jdk1.7和jdk1.8)
发布日期:2021-04-30 21:01:49
浏览次数:110
分类:精选文章
本文共 5621 字,大约阅读时间需要 18 分钟。
Java集合框架-Map接口-HashMap原理和源码
HashMap底层实现原理
HashMap的put操作(以jdk7为例说明)
在实际使用HashMap时,put操作是最常见的操作之一。以下是jdk7版本的put操作流程:
实例化后初始化
创建HashMap对象后,默认会初始化一个长度为16的一维数组Entry[] table。此外,还会设置默认的容量DEFAULT_INITIAL_CAPACITY(16)和负载因子DEFAULT_LOAD_FACTOR(0.75)。调用put(K key, V value)
在put方法中,首先判断当前底层数组是否为空。如果为空,则调用inflateTable方法进行初始化或扩容。处理null键
如果key为null,直接调用putForNullKey方法,这种情况由于null键无法调用hashCode方法,所以默认存储在table[0]位置。计算哈希值
调用hash(Object k)方法计算key的哈希值。jdk7中,哈希值计算采用特定的算法,确保不同哈希值的冲突概率较低。计算存储位置
使用indexFor方法计算实际存储位置。该方法通过将哈希值与数组长度-1进行按位与运算,得到最终的存储位置。处理已有键
遍历table数组,检查是否存在与新key哈希值相同的节点。如果存在且key相等,则替换旧的值;如果只哈希值相同,但key不等,则替换旧值并返回旧值。新键处理
如果不存在相同哈希值的节点,则新建一个Entry对象,将其插入table数组的适当位置。扩容判断
在添加新键时,判断当前大小是否超过扩容阈值threshold。如果超过,则调用resize方法进行扩容。jdk7与jdk8的区别
初始化方式
- jdk7:在实例化时,底层数组
table初始化为空数组Entry[]。 - jdk8:底层数组
table初始化为空数组Node[]。在第一次调用put方法时,底层数组会被初始化为长度为16。
底层结构
- jdk7:仅支持数组+链表结构。
- jdk8:支持数组+链表+红黑树结构。当链表长度超过
TREEIFY_THRESHOLD(8)且数组长度大于64时,链表会转化为红黑树。
扩容机制
- jdk7:默认扩容至原容量的两倍。
- jdk8:当链表长度超过
TREEIFY_THRESHOLD且容器大小超过MIN_TREEIFY_CAPACITY(64)时,链表会转化为红黑树。
HashMap源码解析
常量定义
DEFAULT_INITIAL_CAPACITY:默认初始化容量,值为16。MAXIMUM_CAPACITY:最大支持容量,值为2^30。DEFAULT_LOAD_FACTOR:默认负载因子,值为0.75。TREEIFY_THRESHOLD:链表长度大于该值时,转化为红黑树。UNTREEIFY_THRESHOLD:红黑树存储的节点数小于该值时,退化为链表。MIN_TREEIFY_CAPACITY:红黑树存储的节点数小于该值时,转化为链表。
构造器分析
jdk7构造器
public HashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);} 在空参构造器中,调用HashMap(int initialCapacity, float loadFactor)构造器,初始化容量和负载因子。
jdk8构造器
public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR;} 在空参构造器中,直接设置负载因子为默认值DEFAULT_LOAD_FACTOR,其他字段在第一次put操作时初始化。
put方法源码解析(jdk8)
put方法调用链
public V put(K key, V value) { return putVal(hash(key), key, value, false, true);} putVal方法
private V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node[] tab = table; int n = tab.length; int i = (n - 1) & hash; Node p = tab[i]; if (tab == null || n == 0) { n = resize(); tab = table = new Node[n]; p = tab[i = (n - 1) & hash]; } if (p == null) { tab[i] = newNode(hash, key, value, null); if (n > 0 && (n < MIN_TREEIFY_CAPACITY || (size > 64 && (size > 8 * n)))) { treeifyBin(tab, hash); } size++; if (size > threshold) { resize(); } return null; } if (p.hash == hash && (p.key == null ? key == null : key.equals(p.key))) { e = p; } else if (p instanceof TreeNode) { e = ((TreeNode) p).putTreeVal(this, tab, hash, key, value); } else { for (int binCount = 0; ; ++binCount) { if (e = p.next) { if (e.hash == hash && (e.key == null ? key == null : key.equals(e.key))) { break; } } else { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) { treeifyBin(tab, hash); } break; } } if (e != null) { old = e.value; if (!onlyIfAbsent || old == null) { e.value = value; } afterNodeAccess(e); return old; } } modCount++; size++; if (size > threshold) { resize(); } afterNodeInsertion(evict); return null;} resize方法
private void resize() { Node[] oldTab = table; int oldCap = oldTab == null ? 0 : oldTab.length; int oldThr = threshold; int newCap = oldCap < MAXIMUM_CAPACITY ? oldCap << 1 : MAXIMUM_CAPACITY; int newThr = oldCap < MAXIMUM_CAPACITY ? (int) (oldCap * loadFactor) : Integer.MAX_VALUE; Node[] newTab = new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node e = oldTab[j]; if (e == null) { continue; } oldTab[j] = null; if (e.next == null) { int i = e.hash & (newCap - 1); newTab[i] = e; } else if (e instanceof TreeNode) { TreeNode hd = null, tl = null; do { TreeNode p = replacementTreeNode(e, null); if (tl == null) { hd = p; } else { p.prev = tl; tl.next = p; } tl = p; } while ((e = p.next) != null); if (hd != null) { hd.treeify(newTab); } } else { int loHead = null, loTail = null; int hiHead = null, hiTail = null; Node next = e.next; do { 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); loTail.next = null; newTab[j] = loHead; if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } threshold = newThr;} 总结
HashMap作为Java集合框架中的核心类,其底层实现采用数组+链表(或红黑树)的结构,通过哈希函数实现高效的插入、删除和查找操作。其默认容量为16,负载因子为0.75,扩容时会将当前容量的两倍进行扩展。在jdk8中,进一步优化了链表到红黑树的转化机制,提升了性能和并发安全性。
发表评论
最新留言
做的很好,不错不错
[***.243.131.199]2026年06月16日 17时24分17秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
php验证码背景色设置无效
2023-03-02
php验证邮箱是否有效
2023-03-02
PHP高性能分布式应用服务器框架-SwooleDistributed
2023-03-02
PHP高效、轻量级表格数据处理库 OpenSpout
2023-03-02
R 数据缺失的处理
2023-03-02
php,nginx重启
2023-03-02
php:$_ENV 和 getenv区别
2023-03-02
PHP:PDOStatement::bindValue参数类型php5和php7问题
2023-03-02
Q媒体播放器.如何播放具有多个音频的视频?
2023-03-02
pickle
2023-03-02
Pickle thread.lock(Pymongo)
2023-03-02
pickle模块
2023-03-02
qYKVEtqdDg
2023-03-02
pid控制
2023-03-02
PID控制介绍-ChatGPT4o作答
2023-03-02
PID控制器数字化
2023-03-02
Qwen-VL项目使用指南
2023-03-02
PIESDKDoNet二次开发配置注意事项
2023-03-02
PIGS POJ 1149 网络流
2023-03-02