Java Set 集合详解

Set 是 Java 集合框架中的一个接口,继承自 Collection,它的特点是不允许存储重复元素,并且可以根据不同实现类选择是否保证元素的顺序


1. Set 的特点

  • 不允许重复元素:如果向 Set 中添加一个已存在的元素,添加操作会被忽略。
  • 无索引:不像 ListSet 不支持索引访问,只能通过迭代器或增强 for 循环遍历。
  • 允许存储 null(但只能有一个 null)。
  • 元素顺序不固定(具体顺序由实现类决定)。

2. Set 的主要实现类

实现类 底层数据结构 特点
HashSet 哈希表HashMap 无序,增删查快(O(1)
LinkedHashSet 哈希表 + 双向链表 有序(按插入顺序)
TreeSet 红黑树(自平衡二叉树) 有序(按自然排序比较器排序

3. Set 的常用方法

Set 继承了 Collection 接口的方法,没有额外的方法,常见操作包括:

方法 作用
add(E e) 添加元素(如果存在则不添加)
remove(Object o) 删除指定元素
contains(Object o) 判断是否包含指定元素
size() 获取集合大小
isEmpty() 判断集合是否为空
clear() 清空集合
iterator() 获取迭代器

4. HashSet 用法

HashSet 基于 哈希表 实现,不保证元素顺序,插入、删除、查找的时间复杂度 O(1)

4.1 HashSet 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.HashSet;
import java.util.Set;

public class HashSetExample {
public static void main(String[] args) {
Set<String> set = new HashSet<>();

set.add("Java");
set.add("Python");
set.add("C++");
set.add("Java"); // 重复元素不会被添加

System.out.println(set); // [C++, Java, Python](无序)
System.out.println("是否包含 Java: " + set.contains("Java")); // true

set.remove("Python");
System.out.println(set); // [C++, Java]
}
}

4.2 遍历 HashSet

1
2
3
4
5
6
7
8
9
10
11
12
13
// 方式 1:增强 for 循环
for (String item : set) {
System.out.println(item);
}

// 方式 2:迭代器遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

// 方式 3:Lambda 表达式
set.forEach(System.out::println);

5. LinkedHashSet 用法

LinkedHashSet 继承自 HashSet但它保留了元素的插入顺序

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.LinkedHashSet;
import java.util.Set;

public class LinkedHashSetExample {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<>();

set.add("Java");
set.add("Python");
set.add("C++");

System.out.println(set); // [Java, Python, C++](有序)
}
}

6. TreeSet 用法

TreeSet 基于 红黑树 实现,保证元素按照自然顺序(默认升序)排序,增删查的时间复杂度 O(log n)

6.1 TreeSet 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Set;
import java.util.TreeSet;

public class TreeSetExample {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();

set.add(10);
set.add(5);
set.add(20);
set.add(15);

System.out.println(set); // [5, 10, 15, 20](自动排序)
}
}

6.2 自定义排序

可以使用 Comparator 来指定排序规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class CustomTreeSet {
public static void main(String[] args) {
// 按降序排序
Set<Integer> set = new TreeSet<>(Comparator.reverseOrder());

set.add(10);
set.add(5);
set.add(20);
set.add(15);

System.out.println(set); // [20, 15, 10, 5]
}
}

7. Set 常见问题

7.1 HashSet 为什么无序?

HashSet 基于 HashMap,元素存储在 哈希桶(Hash Bucket) 里,顺序依赖于哈希值,不会按插入顺序排列

7.2 如何让 Set 保持顺序?

  • 按插入顺序:使用 LinkedHashSet
  • 按大小排序:使用 TreeSet

7.3 TreeSet 为什么不能存 null

TreeSet 依赖 compareTo() 进行排序,而 null 不能参与比较,因此 TreeSet 不允许 null 元素


8. Set vs List 对比

特性 Set List
是否允许重复 ❌ 不允许 ✅ 允许
是否有序 ❌(HashSet)✅(LinkedHashSetTreeSet ✅ 保持插入顺序
是否支持索引访问
查询效率 HashSet O(1) ❌ O(n)
插入/删除效率 HashSet O(1) ❌ O(n)

9. 总结

实现类 特点
HashSet 无序,查询快,适用于去重
LinkedHashSet 保持插入顺序
TreeSet 按自然顺序或自定义排序

如果需要唯一性 + 高效查询,使用 HashSet
如果需要唯一性 + 顺序,使用 LinkedHashSet
如果需要唯一性 + 排序,使用 TreeSet