详解:Set集合是如何保证元素不重复的

Set集合通过以下机制确保元素不重复:

一、Set接口的设计原则

Set接口继承自Collection,强制要求实现类不允许包含重复元素。其核心约束为:

  • 唯一性:每个元素在Set中只能出现一次。
  • 无索引 :不提供基于索引的访问方法(如get(int index))。

二、实现类机制解析

不同的Set实现类通过不同方式确保元素唯一性:

1. HashSet(基于哈希表)

  • 底层结构 :基于HashMap实现,元素作为HashMap的键存储。

  • 唯一性判断

    • 哈希码(hashCode) :调用元素的hashCode()方法确定存储位置。
    • 相等性(equals) :哈希冲突时,调用equals()方法比较元素内容。
  • 添加流程

    1. 计算元素哈希值,定位到哈希桶。
    2. 若桶为空,直接存入。
    3. 若桶非空,遍历链表/红黑树,通过equals()比较是否存在重复元素。
    4. 若存在重复,丢弃新元素;否则插入链表/树。
  • 代码示例

    typescript 复制代码
    java
    public boolean add(E e) {
        return map.put(e, PRESENT) == null; // HashMap的键唯一
    }

2. TreeSet(基于红黑树)

  • 底层结构 :基于TreeMap实现,元素按自然顺序或Comparator排序。

  • 唯一性判断

    • 比较结果(compareTo/compare) :通过ComparableComparator比较元素。
    • 相等性定义 :当compareTo()compare()返回0时,视为重复。
  • 添加流程

    1. 通过比较器确定元素位置。
    2. 若比较结果为0,替换或丢弃元素(根据实现逻辑)。
    3. 若比较结果非0,插入到合适位置以维持排序。
  • 注意事项

    • 与equals()的一致性 :若compareTo()equals()逻辑不一致,可能导致元素被视为重复但equals返回false。

3. LinkedHashSet(有序哈希集合)

  • 底层结构:继承自HashSet,通过双向链表维护插入顺序。
  • 唯一性判断:与HashSet相同(依赖hashCode和equals)。

三、关键方法的作用

  1. hashCode()

    • 确定元素在哈希表中的存储位置。
    • 若哈希码冲突,需进一步通过equals()判断是否重复。
  2. equals()

    • 精确判断两个对象是否逻辑相等。
    • 必须满足自反性、对称性、传递性和一致性。
  3. compareTo() / compare()

    • 在TreeSet中定义元素的排序和唯一性。
    • 返回0时视为重复,无论equals()结果如何。

四、用户自定义类的注意事项

  1. 重写hashCode()和equals()

    • 规则 :若a.equals(b)为true,则a.hashCode() == b.hashCode()必须成立。

    • 示例

      typescript 复制代码
      java
      public class Person {
          private String name;
          private int age;
      
          @Override
          public int hashCode() {
              return Objects.hash(name, age); // 基于属性生成哈希码
          }
      
          @Override
          public boolean equals(Object obj) {
              if (this == obj) return true;
              if (obj == null || getClass() != obj.getClass()) return false;
              Person person = (Person) obj;
              return age == person.age && Objects.equals(name, person.name);
          }
      }
  2. 实现Comparable或提供Comparator

    • TreeSet要求 :元素必须可比较,否则抛出ClassCastException

    • 示例

      csharp 复制代码
      java
      // 自然排序
      public class Person implements Comparable<Person> {
          @Override
          public int compareTo(Person other) {
              return this.name.compareTo(other.name);
          }
      }
      
      // 定制Comparator
      TreeSet<Person> set = new TreeSet<>(Comparator.comparingInt(Person::getAge));

五、常见问题与解决方案

问题场景 原因 解决方案
HashSet添加重复元素 未正确重写hashCode和equals方法 检查并正确实现hashCode()和equals()
TreeSet抛出ClassCastException 元素未实现Comparable且无Comparator 提供Comparator或实现Comparable接口
比较逻辑与equals不一致 compareTo()与equals()结果冲突 确保两者逻辑一致

六、总结

  • HashSet :依赖哈希表和equals()/hashCode(),适合快速查找。
  • TreeSet:依赖红黑树和比较器,适合有序遍历。
  • LinkedHashSet:在HashSet基础上维护插入顺序。
  • 核心原则:正确实现元素的哈希码、相等性及比较逻辑,确保Set正确识别重复元素。

七、注意事项

  1. 哈希冲突≠重复元素:哈希值相同但内容不同的对象会被放到同一桶(链表或树中)。
  2. 不可变对象更安全:若对象存入Set后发生修改,可能导致哈希值变化,引发内存泄漏或重复元素。
  3. 线程不安全 :多线程操作需用Collections.synchronizedSet()ConcurrentHashMap.newKeySet()包装。
相关推荐
小莫分享几秒前
github 镜像节点
java
链上Sniper9 分钟前
智能合约状态快照技术:实现 EVM 状态的快速同步与回滚
java·大数据·linux·运维·web3·区块链·智能合约
androidwork22 分钟前
深入解析内存抖动:定位与修复实战(Kotlin版)
android·kotlin
梦天201534 分钟前
android核心技术摘要
android
缘来是庄1 小时前
设计模式之建造者模式
java·设计模式·建造者模式
小湘西1 小时前
Apache HttpClient 的请求模型和 I/O 类型
java·http·apache
小张成长计划..1 小时前
数据结构-栈的实现
开发语言·数据结构
沃夫上校1 小时前
Feign调Post接口异常:Incomplete output stream
java·后端·微服务
q567315231 小时前
Java Selenium反爬虫技术方案
java·爬虫·selenium
张小洛1 小时前
Spring IOC容器核心阶段解密:★Bean实例化全流程深度剖析★
java·后端·spring·ioc容器·bean实例化