java中set集合天然支持去重和快速查找,核心在于选择合适的实现类:hashset适合大多数场景,treeset适合需要排序的场景,linkedhashset适合需保持插入顺序的场景。

HashSet:基于哈希表的高效去重与查找
HashSet底层用HashMap存储元素,添加、查找、删除平均时间复杂度为O(1),但不保证顺序。关键前提是元素正确重写hashCode()和equals()方法。
- 对于自定义对象(如Person),必须重写hashCode()和equals(),否则相同逻辑内容的对象可能被重复添加
- 添加重复元素时,add()方法返回false,不会报错,也不会覆盖原值
- 避免将null插入HashSet(虽然允许,但易引发空指针,建议提前校验)
TreeSet:有序去重,支持范围查找
TreeSet基于红黑树实现,自动按自然顺序或指定Comparator排序,去重同时保持有序。查找、插入、删除时间复杂度为O(log n)。
- 元素必须实现Comparable接口,或构造时传入Comparator,否则运行时抛ClassCastException
- 支持subSet()、headSet()、tailSet()等范围操作,适合“查年龄在20~30之间的人”这类需求
- 不接受null值(除非Comparator显式处理null),否则add()时直接抛NullPointerException
LinkedHashSet:兼顾插入顺序与去重效率
LinkedHashSet继承HashSet,内部维护双向链表记录插入顺序,迭代时按添加顺序返回,性能略低于HashSet(因额外链表开销),但仍是O(1)均摊复杂度。
- 适合需要去重又要求遍历结果与输入顺序一致的场景,比如读取配置项、解析日志行
- 序列化/反序列化行为与HashSet一致,但重建后仍保持原始插入顺序
- 与HashSet一样依赖hashCode()和equals(),不可忽略重写
实用技巧与避坑提醒
实际开发中,仅靠Set声明类型不够,需结合业务选型并注意边界情况。
- 不要用Set存储可变对象(如含可变字段的实体),否则修改后可能导致hash码变化,造成contains()失效或元素丢失
- 从List转Set去重推荐用new HashSet(list),比循环add()更简洁;若需保留顺序,改用new LinkedHashSet(list)
- 判断是否存在大量重复时,可先用size比较:if (new HashSet(list).size() == list.size()) 表示无重复
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











