HashSet不保证顺序,TreeSet按自然序或自定义序排列;前者基于HashMap、O(1)操作、允许null、内存省;后者基于红黑树、O(log n)操作、要求可比较、支持范围查询、不允许null(除非Comparator处理)。

HashSet 不保证顺序,TreeSet 按自然序或自定义序排列
这是最直观的差异:HashSet 底层用 HashMap 实现,元素插入后位置由 hashCode() 和哈希表桶分布决定,遍历时顺序不可预测;TreeSet 底层是红黑树(TreeMap),默认按元素的 compareTo() 结果升序排列,或接受 Comparator 自定义排序逻辑。
常见错误现象:把 HashSet 当作“轻量版 TreeSet”来用,期望输出有序结果,结果每次运行顺序都不同,甚至在不同 JDK 版本间表现不一致。
- 如果业务依赖遍历顺序(比如日志聚合、前端展示),必须用
TreeSet或显式转成ArrayList后Collections.sort() -
TreeSet要求元素可比较:要么实现Comparable,要么构造时传Comparator,否则 add 时抛ClassCastException -
HashSet允许存null(仅一个),TreeSet默认不允许null(会抛NullPointerException),除非Comparator显式处理null
时间复杂度差异:O(1) vs O(log n)
HashSet 的 add()、contains()、remove() 平均是 O(1),依赖哈希函数质量;TreeSet 对应操作都是 O(log n),因为要维持红黑树平衡。
性能影响明显体现在大数据量高频查询场景:比如实时风控系统中判断用户 ID 是否在黑名单里,用 HashSet 比 TreeSet 快 3–5 倍(实测百万级数据)。
立即学习“Java免费学习笔记(深入)”;
- 哈希冲突严重时(如大量对象
hashCode()返回相同值),HashSet退化为链表/红黑树查找,最坏 O(n) -
TreeSet的 log n 是稳定上界,适合对 worst-case 延迟敏感的系统(如金融交易中间件) - 频繁增删 + 需要有序遍历?考虑先用
HashSet操作,最后一次性转TreeSet或sortedSet = new TreeSet(set)
内存占用:HashSet 更省,TreeSet 多存父子/颜色等树节点字段
每个 TreeSet 节点除了存元素引用,还要维护 left/right/parent 指针和 color 标志位,典型比 HashSet 节点多占 24–32 字节(64 位 JVM,开启指针压缩)。
容易被忽略的点:当集合长期存活且元素数达十万级以上,内存差异会放大。例如存储 50 万个 String,TreeSet 可能多占 15–20 MB 堆内存。
- 若只用
size()和isEmpty(),两者开销几乎一样;但只要涉及迭代或范围查询(如subSet()),TreeSet的结构优势才体现 -
HashSet初始容量设太小(如默认 16)会导致多次 rehash;TreeSet无容量概念,但构造时传Comparator有额外对象开销
TreeSet 支持范围查询,HashSet 完全不支持
TreeSet 提供 headSet()、tailSet()、subSet() 等方法,能高效获取某段区间内的元素——底层直接走红黑树的中序遍历剪枝,不是遍历全集再过滤。
典型误用:有人对 HashSet 先转 ArrayList 再用 stream().filter() 做范围筛选,数据量一大就卡顿,其实该换 TreeSet。
-
subSet(fromElement, toElement)是半开区间:包含fromElement,不包含toElement - 所有范围视图都是“实时”的:原
TreeSet修改后,对应视图自动反映变化 -
HashSet没有等价 API;强行模拟需全量遍历,O(n) 成本无法避免










