995 字
5 分钟
HashSet、TreeSet 与有序 Map:去重、排序和使用边界

选择 Set 和 Map 时,先问三件事:是否需要去重,是否需要保持插入顺序,是否需要按大小排序?“有序”不是一个统一含义。

HashSet 怎样去重?#

HashSet 常见实现基于 HashMap,把元素作为 key,value 使用固定占位对象。添加时先通过 hashCode 定位,再用 equals 判断是否已存在。

两个业务相等的对象必须同时满足正确的 equals/hashCode 约定。普通 HashSet 不保证遍历顺序,不能把某次测试中的顺序当作稳定接口。

HashSet、LinkedHashSet、TreeSet#

类型去重与组织方式遍历特点
HashSet哈希结构,依赖 equals/hashCode无顺序保证
LinkedHashSet哈希结构加顺序链接通常保持插入顺序
TreeSet有序树,使用自然顺序或 Comparator按比较规则排序

TreeSet 通过比较结果是否为 0 判断同一个集合元素。如果 Comparator 只比较年龄,同龄但不同姓名的两个对象可能被视为同一个元素。比较规则应与业务去重需求一致,并尽量与 equals 一致,避免违反一般 Set 语义。

Comparable 与 Comparator 有什么区别?#

Comparable 由类型自身实现,通过 compareTo 定义自然顺序;Comparator 是独立的比较策略,通过 compare 或组合方法提供不同排序规则。同一种 User 可以按 ID、年龄或姓名排序,不必把所有策略都写进类自身。

class User {
private final int age;
private final String name;
User(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() { return age; }
public String getName() { return name; }
}
Comparator<User> byAgeThenName = Comparator
.comparingInt(User::getAge)
.thenComparing(User::getName);

比较结果为负、零、正,分别表示小于、相同排序位置、大于。不要直接用两个 int 相减作为通用比较实现,极端数值可能溢出;可以使用 Integer.compare 或 comparingInt。

排序列表时,比较为 0 的元素可以同时保留;TreeSet/TreeMap 却用比较为 0 判断集合元素或 key 相同,因此排序规则也会影响去重。在上面的例子中,年龄和姓名都相同的两个 User 会被比较器视为相同位置;若业务需要区分,还要加入相应身份字段。

LinkedHashMap 与 TreeMap#

LinkedHashMap 在 HashMap 基础上维护顺序,默认是插入顺序,也可以通过构造参数启用访问顺序。配合淘汰钩子可以做简单 LRU 示例,但它自身不是线程安全的生产级缓存方案。

TreeMap 按 key 的自然顺序或 Comparator 排序,常见实现为红黑树,基本操作通常 O(log n)。适合有范围查询或顺序访问要求的映射;排序也以比较器为准,不是按 value 排序。

PriorityQueue 是排序后的列表吗?#

不是。PriorityQueue 基于堆结构,保证队头是当前优先级最高的元素;自然顺序下通常是最小元素。不断 poll 可以按优先级取出,直接遍历内部元素不保证整体有序。

它适合任务优先级、Top K 等场景,不保证线程安全。如果需要多个线程共享,要考虑相应的并发实现或同步策略。

面试回答模板#

HashSet 基于哈希结构,用 equals/hashCode 去重;LinkedHashSet 额外保持插入顺序;TreeSet 按比较规则排序,比较为 0 的元素会被视为同一个元素。Comparable 定义类型自然顺序,Comparator 提供外部排序策略。LinkedHashMap 可保持插入或访问顺序,TreeMap 按 key 排序。PriorityQueue 只保证队头优先级,并不保证遍历结果完整有序。

自测#

  1. HashSet 为什么不能只重写 equals?还必须保证相等对象的 hashCode 相同。
  2. TreeMap 是按插入顺序遍历吗?不是,按 key 的比较规则。
  3. PriorityQueue 的 for-each 遍历是否有序?不保证。
  4. 列表排序与 TreeSet 对比较为 0 的元素处理相同吗?不同,列表可以同时保留,TreeSet 视为同一元素。
HashSet、TreeSet 与有序 Map:去重、排序和使用边界
https://blog.xqcherry.top/posts/interview/java/set-and-ordered-map/
作者
xqcherry
发布于
2026-10-12
许可协议
CC BY-NC-SA 4.0