HashSet适合去重快查,TreeSet适合排序或范围操作;TreeSet存null抛NullPointerException,HashSet允许一个null;自定义类入TreeSet需实现Comparable或传Comparator;HashSet去重须同时重写hashCode()和equals()且逻辑一致;TreeSet的subSet()等返回高效视图,共享数据。

HashSet 适合去重快查,TreeSet 适合要排序或范围操作的场景——选错一个,线上可能慢几倍。
为什么 add() 会抛 NullPointerException?
TreeSet 在插入时必须比较元素大小,而 null 无法参与比较(compareTo() 或 compare() 都会直接抛 NullPointerException)。HashSet 没这个问题,它只调用 hashCode() 和 equals(),null 是合法值(且只能存一个)。
- TreeSet 中存
null→ 运行时报错,不是编译错误 - HashSet 中存
null→ 合法,但后续再 addnull会失败(返回false) - 自定义类想进 TreeSet,要么实现
Comparable,要么构造时传Comparator,否则运行时报ClassCastException
怎么让自定义对象在 HashSet 里不重复?
必须同时重写 hashCode() 和 equals(),且逻辑一致:如果 equals() 返回 true,两个对象的 hashCode() 必须相等。
public class User {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age); // 和 equals 用的字段完全一致
}
}
- 只重
equals()不重hashCode()→ HashSet 当成不同对象,重复添加成功 - 字段改了但没同步更新
hashCode()→ 对象修改后可能从 HashSet 中“消失”(查不到)
TreeSet 的 subSet()、headSet() 真的高效吗?
是的。这些方法返回的是底层红黑树的**视图(view)**,不是新集合,不复制数据,时间复杂度 O(log n),空间 O(1)。但要注意:视图和原 TreeSet 共享数据,修改视图会影响原集。
立即学习“Java免费学习笔记(深入)”;
-
treeSet.subSet(5, true, 10, false)→ 左闭右开区间 [5, 10) - 返回的
NavigableSet仍支持first()、last()、higher()等操作 - 如果原 TreeSet 被并发修改(非线程安全),视图操作可能抛
ConcurrentModificationException
最常被忽略的一点:TreeSet 的排序依赖于比较逻辑的稳定性——如果比较器内部用了可变字段,或者 compareTo() 实现违反了自反性/传递性,集合行为将不可预测,甚至导致 add() 卡死或返回错误结果。










