set不允许重复元素,因其add()方法依赖equals()和hashcode()判断存在性;未重写二者时自定义对象无法去重;treeset则依赖compareto()或comparator,要求元素可比较且非null。

Set为什么不允许重复元素
因为 add() 方法内部依赖 equals() 和 hashCode() 判断是否已存在——两个对象只要 hashCode() 相同且 equals() 返回 true,就被视为同一个元素,添加失败并返回 false。
- 不重写
hashCode()和equals()的自定义类,放进HashSet后大概率无法去重(每个实例的hashCode()默认是内存地址) -
TreeSet不走哈希逻辑,而是用compareTo()或Comparator比较,所以它不要求重写hashCode(),但要求元素可比较,且不能为null - 哪怕只改一个字段,也要同步更新
hashCode()和equals(),否则集合行为会错乱(比如contains()找不到明明加过的对象)
“无序”到底指什么,哪些场景下其实有序
HashSet 的遍历顺序和插入顺序无关,也和自然顺序无关——它由元素 hashCode() 对桶数组取模决定,每次运行甚至可能不同;但这不等于“完全随机”,只是不可预测。
-
LinkedHashSet明确保证插入顺序,适合需要“首次出现即保留”的去重场景(如日志去重、请求参数 dedup) -
TreeSet按排序顺序迭代,适合需要范围查询或天然升序输出的场景(如排行榜前 N 名、时间窗口去重) - 别对
HashSet的toString()输出做任何顺序假设——它只是调试时的偶然表现,不是契约
为什么 Set 没有 get(int index) 方法
因为唯一性 + 无索引是设计原点:Set 关注的是“是否存在”,不是“在第几个位置”。它本质上是简化版的 Map<e object></e>(HashSet 底层就是 HashMap<e present></e>),根本没有位置概念。
- 想按顺序访问?先转成
ArrayList:new ArrayList(set),但要注意这会丢失原始插入/排序语义(除非你用的是LinkedHashSet或TreeSet) - 想查某个元素在集合中的“序号”?说明你误用了 Set——该用
List配合indexOf(),或者用带索引的结构如ArrayList+Set双存 - 用
iterator().next()强行取第一个元素?危险——HashSet的第一个元素毫无业务意义,且并发修改时可能抛ConcurrentModificationException
null 元素能放吗,怎么放才安全
HashSet 和 LinkedHashSet 允许一个 null,TreeSet 直接拒绝,调用 add(null) 会抛 NullPointerException。
立即学习“Java免费学习笔记(深入)”;
- 如果业务逻辑中
null是合法值(如缺省配置项),优先选HashSet或LinkedHashSet - 别在
TreeSet里传null,哪怕你写了Comparator也拦不住——红黑树插入阶段就会判空失败 - 用
contains(null)判断之前,确认实现类支持;更稳妥的做法是统一用Objects.nonNull()做前置校验,避免依赖集合行为
hashCode()”,导致对象在 Set 里既找不着又删不掉——这种 bug 往往要 debug 半天才能定位到。










