Set接口最核心的特点是元素唯一性,通过equals()和hashCode()协同判断重复,自定义类需重写二者;HashSet、TreeSet、LinkedHashSet均保证唯一性但底层不同;null在HashSet/LinkedHashSet中允许一个,在TreeSet中禁止。

Java中Set接口最核心的特点就是元素唯一性,这是它区别于List等其他集合的根本所在。添加重复元素不会报错,但也不会生效——add()方法直接返回false,原集合保持不变。
唯一性靠equals()和hashCode()共同保障
Set不单看对象内容是否相同,而是有一套协同判断机制:
- 先调用元素的hashCode()方法,快速定位可能的位置(比如哈希桶)
- 如果多个元素哈希值相同(哈希冲突),再调用equals()逐个比对内容
- 只有当两个方法都“一致”时,才认定为重复元素
- 自定义类用作Set元素时,必须同时重写hashCode()和equals(),否则可能无法正确去重
不同实现类对唯一性的处理逻辑一致,但底层不同
虽然HashSet、TreeSet、LinkedHashSet表现不同(如顺序、null支持),但“拒绝重复”的行为完全统一:
- HashSet:基于HashMap的key机制,利用哈希表结构实现O(1)平均插入/查找
- TreeSet:基于红黑树,通过compareTo()或Comparator比较大小来判定是否重复,时间复杂度O(log n)
- LinkedHashSet:本质是HashSet+双向链表,既保证唯一性,又维护插入顺序
null元素的特殊规则
null在Set中被当作一个合法值对待,但有明确限制:
立即学习“Java免费学习笔记(深入)”;
- HashSet和LinkedHashSet允许且仅允许一个null元素
- TreeSet不允许null,插入时会抛出NullPointerException
- 这是因为TreeSet依赖比较操作,而null.compareTo(...)或comparator.compare(null, x)都会失败
无索引、无序,但“唯一”始终优先
Set不提供get(int index)这类方法,也不保证遍历顺序(除非用LinkedHashSet或TreeSet):
- 你不能靠位置找元素,只能靠内容查——这反过来强化了“按值唯一”的设计哲学
- 即使遍历顺序看起来随机(如HashSet),只要两次add("abc")只存一个,就说明唯一性已生效
- 去重场景下,不必关心顺序;需要顺序时,选对实现类即可,不影响唯一性逻辑










