【Java集合】Set集合背后的秘密:为什么它比List接口更严格?
字数 1536 2025-08-10 08:28:09

Java Set集合详解:特性、实现与底层原理

一、Set集合概述

Set接口是Java集合框架中重要的组成部分,它与List接口一样继承自Collection接口,但具有以下显著特性:

  1. 元素唯一性:Set集合不允许包含重复元素
  2. 无序性:元素存储顺序与添加顺序无关(LinkedHashSet除外)
  3. 无索引:不能通过索引访问元素,不能使用普通for循环遍历

二、Set集合常用实现类

1. HashSet

基本特性

  • 基于哈希表实现(实际上是HashMap的实例)
  • 查询速度非常快
  • 元素无序存储
  • 允许null元素

底层数据结构

在JDK不同版本中,HashSet的底层实现有所不同:

  • JDK1.8之前:数组 + 链表
  • JDK1.8及之后:数组 + 链表 + 红黑树

哈希表结构解析

哈希表由以下部分组成:

  1. 数组:存储元素的基础结构
  2. 链表:解决哈希冲突
  3. 红黑树:当链表长度达到阈值(默认为8)时转换为红黑树,提高查询效率

哈希值

  • 哈希值是JDK根据对象地址、字符串或数字计算出的int类型数值
  • 获取方式:通过Object类的hashCode()方法
  • String类重写了hashCode()方法,内容相同的字符串哈希值相同

添加元素流程

  1. 调用对象的hashCode()方法计算哈希值
  2. 根据哈希值计算元素在数组中的存储位置
  3. 判断该位置是否已有元素:
    • 若无,直接存入
    • 若有,比较哈希值:
      • 哈希值不同:存入
      • 哈希值相同:调用equals()方法比较内容:
        • 内容不同:存入
        • 内容相同:不存储

存储自定义对象

要保证HashSet正确识别自定义对象的唯一性,必须:

  1. 重写hashCode()方法
  2. 重写equals()方法

示例:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Student)) return false;
    Student student = (Student) o;
    if (age != student.age) return false;
    return name.equals(student.name);
}

@Override
public int hashCode() {
    int result = name.hashCode();
    result = 31 * result + age;
    return result;
}

2. LinkedHashSet

基本特性

  • HashSet的子类
  • 维护元素插入顺序的双向链表
  • 具有可预知的迭代顺序
  • 性能略低于HashSet

与HashSet的区别

LinkedHashSet在HashSet的基础上增加了一个双向链表,用于记录元素的插入顺序。

使用示例

Set<String> demo = new LinkedHashSet<>();
demo.add("hello");
demo.add("world");
demo.add("ni");
demo.add("hao");
demo.add("hello");

for (String content : demo) {
    System.out.println(content);
}

三、Set集合遍历方式

由于Set没有索引,遍历方式与List不同:

  1. 迭代器遍历
Iterator<String> it = set.iterator();
while(it.hasNext()) {
    System.out.println(it.next());
}
  1. 增强for循环
for(String s : set) {
    System.out.println(s);
}

四、Set与List的主要区别

特性 Set List
元素唯一性 不允许重复 允许重复
顺序性 无序(LinkedHashSet除外) 保持插入顺序
访问方式 无索引,只能迭代访问 有索引,可通过索引访问
实现类 HashSet, LinkedHashSet, TreeSet ArrayList, LinkedList, Vector

五、最佳实践

  1. 需要存储唯一元素时使用Set
  2. 不关心顺序时使用HashSet以获得最佳性能
  3. 需要保持插入顺序时使用LinkedHashSet
  4. 存储自定义对象时务必正确实现hashCode()和equals()方法
  5. 在JDK1.8+环境下,HashSet的性能优化(红黑树)会自动生效

六、总结

Set集合通过哈希表实现提供了高效的元素存储和查询能力,其唯一性和无序性是区别于List的核心特征。理解HashSet的底层实现原理(哈希表、哈希值计算、红黑树转换)对于正确使用Set集合至关重要。LinkedHashSet在HashSet的基础上增加了顺序维护能力,适用于需要保持插入顺序的场景。

Java Set集合详解:特性、实现与底层原理 一、Set集合概述 Set接口是Java集合框架中重要的组成部分,它与List接口一样继承自Collection接口,但具有以下显著特性: 元素唯一性 :Set集合不允许包含重复元素 无序性 :元素存储顺序与添加顺序无关(LinkedHashSet除外) 无索引 :不能通过索引访问元素,不能使用普通for循环遍历 二、Set集合常用实现类 1. HashSet 基本特性 基于哈希表实现(实际上是HashMap的实例) 查询速度非常快 元素无序存储 允许null元素 底层数据结构 在JDK不同版本中,HashSet的底层实现有所不同: JDK1.8之前 :数组 + 链表 JDK1.8及之后 :数组 + 链表 + 红黑树 哈希表结构解析 哈希表由以下部分组成: 数组 :存储元素的基础结构 链表 :解决哈希冲突 红黑树 :当链表长度达到阈值(默认为8)时转换为红黑树,提高查询效率 哈希值 哈希值是JDK根据对象地址、字符串或数字计算出的int类型数值 获取方式:通过Object类的hashCode()方法 String类重写了hashCode()方法,内容相同的字符串哈希值相同 添加元素流程 调用对象的hashCode()方法计算哈希值 根据哈希值计算元素在数组中的存储位置 判断该位置是否已有元素: 若无,直接存入 若有,比较哈希值: 哈希值不同:存入 哈希值相同:调用equals()方法比较内容: 内容不同:存入 内容相同:不存储 存储自定义对象 要保证HashSet正确识别自定义对象的唯一性,必须: 重写hashCode()方法 重写equals()方法 示例: 2. LinkedHashSet 基本特性 HashSet的子类 维护元素插入顺序的双向链表 具有可预知的迭代顺序 性能略低于HashSet 与HashSet的区别 LinkedHashSet在HashSet的基础上增加了一个双向链表,用于记录元素的插入顺序。 使用示例 三、Set集合遍历方式 由于Set没有索引,遍历方式与List不同: 迭代器遍历 : 增强for循环 : 四、Set与List的主要区别 | 特性 | Set | List | |------------|-------------------|------------------| | 元素唯一性 | 不允许重复 | 允许重复 | | 顺序性 | 无序(LinkedHashSet除外) | 保持插入顺序 | | 访问方式 | 无索引,只能迭代访问 | 有索引,可通过索引访问 | | 实现类 | HashSet, LinkedHashSet, TreeSet | ArrayList, LinkedList, Vector | 五、最佳实践 需要存储唯一元素时使用Set 不关心顺序时使用HashSet以获得最佳性能 需要保持插入顺序时使用LinkedHashSet 存储自定义对象时务必正确实现hashCode()和equals()方法 在JDK1.8+环境下,HashSet的性能优化(红黑树)会自动生效 六、总结 Set集合通过哈希表实现提供了高效的元素存储和查询能力,其唯一性和无序性是区别于List的核心特征。理解HashSet的底层实现原理(哈希表、哈希值计算、红黑树转换)对于正确使用Set集合至关重要。LinkedHashSet在HashSet的基础上增加了顺序维护能力,适用于需要保持插入顺序的场景。