Set接口保证元素唯一性但不保证插入顺序,HashSet无序且最快,LinkedHashSet保持插入顺序,TreeSet按自然序或比较器排序;均不允许重复元素,依赖equals()和hashCode()判断重复,非==;不支持索引访问;修改影响哈希值的字段会破坏集合一致性。

Set 接口保证元素唯一性,但不保证插入顺序
Java 中 Set 是一个不允许重复元素的集合接口,底层靠 equals() 和 hashCode() 判断是否重复——只要两个对象 equals() 返回 true,就视为同一元素,后者不会被添加。注意:不是靠 == 比较引用。
常见误区是以为 Set 天然有序。其实只有 LinkedHashSet 保持插入顺序,TreeSet 按自然序或自定义比较器排序,而 HashSet 完全无序(底层哈希表决定)。
-
HashSet:最快增删查(O(1) 平均),但遍历顺序不确定 -
LinkedHashSet:维持插入顺序,性能略低于HashSet(多维护双向链表) -
TreeSet:自动排序,增删查为 O(log n),要求元素可比较(实现Comparable或传Comparator)
向 Set 添加 null 元素需看具体实现类
HashSet 和 LinkedHashSet 允许且仅允许一个 null 元素;TreeSet 默认不允许 null,否则抛 NullPointerException——因为排序时要调用 compareTo(),而 null.compareTo(...) 直接崩溃。
如果硬要用 TreeSet 存 null,得显式传入能处理 null 的 Comparator,比如:
立即学习“Java免费学习笔记(深入)”;
new TreeSet(Comparator.nullsFirst(String::compareTo))
但实际开发中,更推荐避免在 TreeSet 中混入 null,逻辑易出错。
Set 不提供按索引访问,也没有 get(int index) 方法
Set 接口继承自 Collection,但**刻意不支持随机访问**。它没有 get()、set()、add(int, E) 这类方法。想取某个“第 N 个”元素?不行——因为大多数 Set 实现根本没定义“第几个”的概念。
如果业务真需要按位置取值,说明你可能误用了 Set。应考虑:
- 用
List去重后操作(如list.stream().distinct().collect(Collectors.toList())) - 或先转成数组:
Object[] arr = set.toArray();,再按索引读(但注意顺序不可靠)
修改 Set 中的元素可能破坏唯一性约束
如果往 HashSet 或 LinkedHashSet 中放了一个对象,之后又修改了影响 hashCode() 或 equals() 的字段,该对象在集合中的位置就“找不到了”——既不能被正常 contains() 到,也无法被 remove() 掉,还可能造成内存泄漏。
典型例子:
Setset = new HashSet<>(); Person p = new Person("Alice", 25); set.add(p); p.setAge(30); // 修改了影响 hashCode 的字段 System.out.println(set.contains(p)); // 可能返回 false!
解决办法只有两个:
- 把对象设计成不可变(immutable),如
final字段 + 无 setter - 若必须可变,操作前先
remove(),改完再add()
这个坑在调试时很难定位,尤其当对象被多个集合或缓存引用时。










