Java HashMap size() 方法(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
在 Java 编程中,HashMap
是一种广泛使用的数据结构,它通过键值对(Key-Value)实现高效的数据存储与检索。而 HashMap
中的 size()
方法作为获取集合元素数量的核心方法,其使用场景和底层实现原理值得深入探讨。无论是处理用户数据统计、缓存管理,还是优化算法性能,理解 size()
方法的特性都至关重要。本文将从基础到进阶,结合代码示例与实际案例,带您全面掌握这一方法的使用与原理。
什么是 HashMap?
核心概念
HashMap
是 Java 集合框架中的一个非线程安全、基于哈希表实现的键值对容器。它通过哈希函数计算键的哈希值,并根据该值确定元素的存储位置。其核心特性包括:
- 键唯一性:每个键(Key)必须唯一,重复的键会导致后添加的值覆盖原有的值。
- 无序性:元素存储顺序与插入顺序无关,除非使用
LinkedHashMap
等有序实现。 - 允许空值:可以存储
null
值,但键只能有一个null
。
类比理解:图书馆书架
想象一个图书馆的书架系统:每个书架(桶,Bucket)可以存放多本书(键值对),而书籍的索引号(哈希值)决定了它们的存放位置。HashMap
的工作原理与此类似:通过哈希函数将键映射到特定的“书架”,从而快速定位数据。
size() 方法:获取键值对数量
方法定义与返回值类型
size()
方法是 HashMap
的一个实例方法,用于返回当前集合中键值对的数量。其定义如下:
public int size() {
return size;
}
- 返回值:
int
类型,表示键值对的总数。 - 时间复杂度:
O(1)
,因为HashMap
内部维护了一个计数器size
,直接返回该值即可。
基础用法示例
HashMap<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90); // 添加第一个键值对
scores.put("Bob", 85); // 添加第二个键值对
System.out.println("当前元素数量:" + scores.size()); // 输出:2
通过上述代码,我们可以直观地看到 size()
方法的简单用法。
size() 方法的底层实现原理
计数器 size
的维护
HashMap
内部通过一个名为 size
的变量实时记录元素数量。每当执行以下操作时,该变量会动态更新:
- 添加元素:调用
put()
方法时,若键不存在,则size
自增 1。 - 删除元素:调用
remove()
方法时,size
自减 1。 - 批量操作:如
clear()
会直接将size
置为 0。
扩容对 size 的影响
当 HashMap
的元素数量超过扩容阈值(默认为容量的 0.75 倍)时,会触发扩容操作。扩容时,所有元素会被重新哈希到更大的桶数组中。然而,此过程仅改变元素的存储位置,不会影响键值对的总数,因此 size()
的返回值保持不变。
扩容过程的代码片段
// 简化版扩容逻辑
void resize() {
// 新容量为原容量的 2 倍
int newCapacity = oldCapacity << 1;
Entry[] newTable = new Entry[newCapacity];
// 遍历旧桶,将元素重新分配到新桶
for (Entry e : oldTable) {
if (e != null) {
Entry<K,V> loHead = null, hiHead = null;
Entry<K,V> loTail = null, hiTail = null;
do {
// 根据新容量重新计算哈希索引
int i = indexFor(e.hash, newCapacity);
if ((i & oldMask) == 0) {
if (loHead == null) {
loHead = e;
loTail = e;
} else {
loTail.next = e;
loTail = e;
}
} else {
if (hiHead == null) {
hiHead = e;
hiTail = e;
} else {
hiTail.next = e;
hiTail = e;
}
}
} while ((e = e.next) != null);
}
}
// 更新 table 指向新数组,但 size 不变
table = newTable;
}
从代码中可以看出,扩容仅修改元素的存储结构,而键值对的总数始终由 size
变量准确记录。
实际应用场景与案例分析
场景 1:数据统计与条件判断
假设我们需要统计用户输入的单词数量,并根据数量执行不同操作:
HashMap<String, Integer> wordCount = new HashMap<>();
// 模拟用户输入的单词列表
String[] words = {"apple", "banana", "apple", "orange"};
for (String word : words) {
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
// 根据单词总数选择输出格式
if (wordCount.size() > 3) {
System.out.println("高频词统计:");
} else {
System.out.println("所有词统计:");
}
// 输出各词的计数
for (Map.Entry<String, Integer> entry : wordCount.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
此案例中,size()
方法直接决定了输出格式的判断逻辑。
场景 2:缓存管理中的容量控制
在缓存系统中,size()
方法可用于监控缓存大小,防止内存溢出:
HashMap<String, byte[]> cache = new HashMap<>();
int MAX_CAPACITY = 1000; // 最大允许的键值对数量
public void putToCache(String key, byte[] value) {
if (cache.size() >= MAX_CAPACITY) {
// 清理旧数据或抛出异常
System.out.println("缓存已满,无法添加新条目");
return;
}
cache.put(key, value);
}
通过结合 size()
方法与阈值判断,可以有效控制资源占用。
使用 size() 方法的注意事项
注意点 1:线程安全问题
HashMap
是非线程安全的,若在多线程环境下使用 size()
方法,可能出现以下问题:
- 不一致读取:在扩容或修改元素时,其他线程可能读取到中间状态的
size
值。 - 解决方式:使用
ConcurrentHashMap
或手动加锁。
注意点 2:与迭代器的配合
在遍历 HashMap
时,若动态修改元素数量(如通过 remove()
或 put()
),可能导致 ConcurrentModificationException
。此时,size()
的值可能已变化,但迭代器仍处于旧状态。
// 错误示例:遍历时修改集合
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
if (entry.getValue() < 90) {
scores.remove(entry.getKey()); // 可能引发异常
}
}
// 正确做法:使用迭代器的 remove() 方法
Iterator<Map.Entry<String, Integer>> iterator = scores.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
if (entry.getValue() < 90) {
iterator.remove(); // 安全删除
}
}
总结
通过本文的讲解,我们深入理解了 HashMap size()
方法的定义、实现原理及实际应用。该方法凭借其 O(1)
的时间复杂度和直观的语义,成为 Java 开发中不可或缺的工具。无论是基础的数据统计,还是复杂的缓存管理,掌握 size()
方法的使用场景与潜在风险,都能帮助开发者编写出更高效、稳定的代码。
在后续学习中,建议读者进一步探索 HashMap
的其他核心方法(如 containsKey()
、get()
)及其底层实现,以构建更完整的 Java 集合知识体系。