HashSet的contains平均O(1),依赖hashCode和equals;TreeSet为稳定O(log n),基于红黑树比较;LinkedHashSet复用HashSet逻辑,同样平均O(1)且保持插入顺序。

Set 集合的 contains 操作是否高效,取决于具体实现类——Java 中常见的 HashSet、TreeSet 和 LinkedHashSet 底层机制完全不同,时间复杂度和行为逻辑差异显著。
HashSet:基于哈希表,平均 O(1),依赖 hashCode() 和 equals()
HashSet.contains(o) 实际委托给内部的 HashMap(JDK 8+ 中以 HashMap 存储键,值固定为 PRESSENT)。流程如下:
- 先计算对象
o.hashCode(),取模定位到哈希桶(bucket)索引; - 若桶为空,直接返回
false; - 若桶非空,遍历该桶中的节点(链表或红黑树);
- 对每个节点,先用
==快速比对引用,再用equals()判定逻辑相等; - 任一节点满足
equals()返回true,则整体返回true。
⚠️ 注意:若自定义类用作 HashSet 元素,必须同时重写 hashCode() 和 equals(),且二者逻辑一致——否则 contains 可能漏判(哈希值不同进错桶)或误判(哈希相同但 equals 不成立)。
TreeSet:基于红黑树,稳定 O(log n),依赖 Comparable 或 Comparator
TreeSet.contains(o) 本质是红黑树的查找操作,不使用 hashCode(),而是按元素自然序(或指定比较器)进行二分路径导航:
- 从根节点开始,调用
compareTo()(或compare())与当前节点比较; - 根据返回值正负决定向左子树或右子树递归;
- 找到相等节点(
compareTo == 0)即返回true; - 到达叶子空节点仍未匹配,则返回
false。
⚠️ 注意:元素必须可比较。若传入 null 且集合不允许(如未指定 Comparator 且元素无 Comparable 实现),会抛 NullPointerException 或 ClassCastException。
LinkedHashSet:继承 HashSet,性能同 HashSet,但维持插入顺序
LinkedHashSet 的 contains 完全复用 HashSet 的逻辑,底层仍是哈希表,只是节点额外维护双向链表指针记录插入顺序。因此:
- 时间复杂度仍为平均 O(1),最坏 O(n)(哈希冲突严重时退化为链表遍历);
- 同样要求正确实现
hashCode()和equals(); - 顺序信息对
contains无影响——它不遍历链表,只走哈希查找路径。
其他细节:null 支持与并发场景
多数 Set 实现允许 null(HashSet、LinkedHashSet 支持一个 null;TreeSet 默认不支持,除非显式传入可处理 null 的 Comparator)。
在并发环境下,普通 Set 非线程安全:contains 虽是读操作,但若其他线程正在结构修改(如 add 触发扩容),可能读到不一致状态。需改用 Collections.synchronizedSet() 或 ConcurrentHashMap.newKeySet()(JDK 8+)等线程安全替代方案。

















