HashSet底层基于HashMap实现,元素作key、PRESENT作value,继承其线程不安全、扩容机制等特性;TreeSet基于TreeMap,依赖Comparable或Comparator排序,null处理需谨慎;二者迭代顺序根本不同。

HashSet底层用的是HashMap,不是哈希表裸实现
很多人以为 HashSet 是直接基于数组+链表/红黑树的哈希表,其实它内部完全复用 HashMap:每个元素作为 HashMap 的 key,而统一使用一个空对象 PRESENT 作为 value。这意味着:
-
HashSet的线程不安全、扩容机制、树化阈值(8)、负载因子(默认0.75f)全部继承自HashMap - 调用
add(e)实际执行的是map.put(e, PRESENT) != null,所以重复添加返回false - 不能存
null元素?错——HashSet可以存一个null,因为HashMap允许一个nullkey
TreeSet默认按自然序排序,但别忽略Comparator的null安全性
TreeSet 底层是 TreeMap,依赖元素的可比较性。如果你传入自定义类且没实现 Comparable,又没提供 Comparator,运行时会抛 ClassCastException,错误信息类似:java.lang.ClassCastException: YourClass cannot be cast to java.lang.Comparable。
更隐蔽的问题是 Comparator 对 null 的处理:
- 若用
Comparator.nullsFirst()或Comparator.nullsLast()包装,null可被安全比较 - 若手写
(a, b) -> a.compareTo(b)且 a 或 b 为null,会触发NullPointerException -
TreeSet不允许插入null(除非你显式传入支持null的Comparator)
HashSet和TreeSet在迭代顺序上的根本差异
HashSet 迭代顺序**不保证稳定**,哪怕两次插入相同元素,顺序也可能不同——因为取决于哈希值、扩容时机、JDK版本(如 JDK 8 引入红黑树后,桶内结构变化会影响遍历顺序)。而 TreeSet 总是按排序顺序(自然序或 Comparator 定义的序)迭代。
Android开发技巧合集pdf版,内容包括:ANDROID常用类库说明,ANDROID文件系统与应用程序架构,ANDROID应用程序结构,ANDROID UI LAYOUT(布局),ANDROID UI 控件,ANDROID UI 美化,ANDROID UI 动画,异步调用,数据存储与读取等。
立即学习“Java免费学习笔记(深入)”;
常见误用场景:
- 用
HashSet存日志事件,然后靠迭代顺序做“时间先后”判断 → 错,应改用LinkedHashSet或加时间戳字段排序 - 单元测试里断言
set1.equals(set2)后,再用assertEquals(list1, new ArrayList(set))比较顺序 → 不可靠,ArrayList构造依赖迭代顺序,而HashSet迭代无序
性能敏感场景下,别只看“O(1) vs O(log n)”
理论复杂度上,HashSet 查找是均摊 O(1),TreeSet 是 O(log n)。但实际中:
- 小数据量(
n )时,TreeSet的常数开销可能更低,因为避免了哈希计算和可能的扩容重散列 - 如果元素的
hashCode()实现低效(比如每次调用都解析 JSON 字符串),HashSet的 “O(1)” 就成了假象 -
TreeSet支持ceiling()、floor()、subSet()等范围操作,HashSet完全不支持——这时候选型要看功能需求,不是纯比速度
Set有些边界行为(比如set = new TreeSet<>(Comparator.reverseOrder()); set.add(3); set.add(1); set.add(4); System.out.println(set); // [4, 3, 1] —— 降序,不是插入顺序
null 处理、扩容触发时机、红黑树退化条件)在 JDK 版本间有细微差异,线上环境建议固定 JDK 小版本并做实测。









