源本科技 | 码上会

Java Set

2026/01/28
41
0

学习目标

  • 理解 Set 接口的核心特性:元素唯一性

  • 掌握 Set 的常用实现类及其适用场景

  • 熟练使用 Set 的基本操作(添加、查询、删除、遍历)

  • 能够根据需求选择合适的 Set 实现类


什么是 Set

在 Java 中,Set 是集合框架中的一个核心接口,位于 java.util 包中。它代表一个不包含重复元素的集合。

核心特性

  • 不允许重复元素:尝试添加已存在的元素将被忽略(add() 返回 false

  • 最多包含一个 null 元素TreeSet 除外,它完全不允许 null

  • 提供高效的查找、插入和删除操作(具体性能取决于实现类)

  • 无索引概念:不能通过位置访问元素(不像 List

注意:Set 是接口,不能直接实例化,必须使用其实现类创建对象。


Set 的继承体系

主要实现类

实现类

底层结构

是否有序

是否允许 null

特点

HashSet

哈希表

❌ 无序

✅ 允许一个

最快的插入 / 查找 / 删除

LinkedHashSet

哈希表 + 双向链表

✅ 按插入顺序

✅ 允许一个

保持插入顺序

TreeSet

红黑树

✅ 自然排序或自定义排序

❌ 不允许

元素自动排序

EnumSet

位向量

✅ 按枚举声明顺序

❌ 不允许

专为枚举类型优化,性能极高


创建 Set 对象

由于 Set 是接口,必须通过其实现类创建实例。推荐使用泛型确保类型安全:

// 使用 HashSet(最常见)
Set<String> set = new HashSet<>();

// 使用 LinkedHashSet(需保持插入顺序)
Set<Integer> orderedSet = new LinkedHashSet<>();

// 使用 TreeSet(需自动排序)
Set<String> sortedSet = new TreeSet<>();

常见操作示例

1. 添加元素(自动去重)

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Set<String> s = new HashSet<>();
        s.add("B");
        s.add("B"); // 重复,不会添加
        s.add("C");
        s.add("A");

        System.out.println(s); // 输出: [A, B, C](顺序不定)
    }
}

add() 方法返回 boolean:成功添加返回 true,已存在返回 false


2. 查询元素

使用 contains() 判断元素是否存在:

Set<String> h = new HashSet<>();
h.add("A");
h.add("B");
h.add("C");

System.out.println("Set is " + h);           // [A, B, C]
System.out.println("Contains D: " + h.contains("D")); // false

3. 删除元素

h.remove("B"); // 删除元素 "B"
System.out.println(h); // [A, C, D, E]

4. 遍历元素

推荐使用增强 for 循环(foreach):

for (String value : h) {
    System.out.print(value + ", ");
}
// 输出: A, B, C, D, E, (顺序取决于具体实现)

也可以使用 Iterator

Iterator<String> it = h.iterator();
while (it.hasNext()) {
    System.out.print(it.next() + " ");
}

常用方法

方法

描述

add(E e)

添加元素(若不存在),返回是否成功

addAll(Collection<? extends E> c)

添加集合中的所有元素

remove(Object o)

删除指定元素

contains(Object o)

判断是否包含某元素

size()

返回元素数量

isEmpty()

判断是否为空

clear()

清空所有元素

iterator()

返回迭代器

containsAll(Collection<?> c)

判断是否包含给定集合的所有元素

removeAll(Collection<?> c)

删除与给定集合共有的所有元素

retainAll(Collection<?> c)

仅保留与给定集合共有的元素(求交集)

toArray()

转换为数组


各实现类使用场景

HashSet

  • 默认选择,当不需要顺序且追求最高性能时

  • 适用于去重、成员检查等场景

LinkedHashSet

  • 需要保持元素插入顺序,同时去重

  • 例如:记录用户最近访问的页面(去重但保留访问顺序)

TreeSet

  • 需要自动排序的唯一元素集合

  • 支持范围查询(如 subSet, headSet, tailSet

  • 例如:排行榜、按字母排序的标签云

EnumSet

  • 仅用于枚举类型

  • 内存效率高,操作极快

  • 例如:权限控制(EnumSet.of(Permission.READ, Permission.WRITE)


重点总结

  • Set 的核心价值在于保证元素唯一性

  • 不同实现类在顺序性、排序、性能、null 支持上各有差异

  • HashSet 是通用首选;LinkedHashSet 保留插入顺序;TreeSet 自动排序

  • 所有 Set 实现类都非线程安全,多线程环境下需外部同步

  • 遍历时应避免使用索引(因为没有索引),优先使用 foreach 或 Iterator


思考题

  1. 为什么 TreeSet 不允许存储 null 元素?这与其底层数据结构有什么关系?

  2. 如果你需要实现一个“最近使用标签”功能(自动去重且按使用时间排序),应该选择哪种 Set 实现?为什么?

  3. 在什么情况下你会使用 retainAll() 方法?请结合实际业务场景举例说明。