Set与HashSet
Set是不重复的集合,本课学习Set的使用和去重原理。
Set接口特点
| 特点 | 说明 |
|---|---|
| 无序 | 元素没有固定顺序(HashSet) |
| 不重复 | 自动去重 |
| 无索引 | 不能通过索引访问 |
HashSet
HashSet基于HashMap实现,是最常用的Set实现。
创建HashSet
JAVA
import java.util.HashSet;
import java.util.Set;
Set<String> set1 = new HashSet<>();
Set<String> set2 = new HashSet<>(100); // 指定容量
Set<String> set3 = new HashSet<>(set1); // 从其他集合创建
基本操作
JAVA
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
// 添加元素
set.add("Alice");
set.add("Bob");
set.add("Charlie");
set.add("Alice"); // 重复,不会添加
System.out.println("集合:" + set); // [Alice, Bob, Charlie](无序)
// 判断
System.out.println("大小:" + set.size()); // 3
System.out.println("是否包含Alice:" + set.contains("Alice")); // true
System.out.println("是否为空:" + set.isEmpty()); // false
// 删除
set.remove("Bob");
System.out.println("删除后:" + set);
// 遍历
for (String name : set) {
System.out.println(name);
}
}
}
去重原理
HashSet通过hashCode和equals判断元素是否重复。
去重流程
TEXT
1. 计算元素的hashCode
2. 如果hashCode不同 → 不重复
3. 如果hashCode相同 → 调用equals比较
4. equals返回true → 重复
5. equals返回false → 不重复(哈希冲突)
示例:自定义对象去重
JAVA
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 必须重写hashCode和equals
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return name + "(" + age + ")";
}
public static void main(String[] args) {
Set<Student> set = new HashSet<>();
set.add(new Student("Alice", 20));
set.add(new Student("Bob", 22));
set.add(new Student("Alice", 20)); // 重复,不会添加
System.out.println("大小:" + set.size()); // 2
System.out.println("集合:" + set);
}
}
⚠️ 重要: 自定义对象存入HashSet时,必须重写hashCode和equals方法,否则无法正确去重。
TreeSet
TreeSet基于红黑树实现,元素自动排序。
特点
| 特点 | 说明 |
|---|---|
| 有序 | 元素按自然顺序或自定义顺序排列 |
| 不重复 | 自动去重 |
| 无索引 | 不能通过索引访问 |
示例:TreeSet
JAVA
import java.util.Set;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
// 自然排序
Set<Integer> numbers = new TreeSet<>();
numbers.add(3);
numbers.add(1);
numbers.add(4);
numbers.add(1); // 重复,不会添加
numbers.add(5);
System.out.println("有序集合:" + numbers); // [1, 3, 4, 5]
// 字符串排序
Set<String> names = new TreeSet<>();
names.add("Charlie");
names.add("Alice");
names.add("Bob");
System.out.println("有序名字:" + names); // [Alice, Bob, Charlie]
}
}
自定义排序
JAVA
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class CustomSortDemo {
public static void main(String[] args) {
// 按长度排序
Set<String> words = new TreeSet<>(Comparator.comparingInt(String::length));
words.add("Hello");
words.add("Hi");
words.add("Java");
words.add("Go");
System.out.println("按长度排序:" + words); // [Go, Hi, Java, Hello]
}
}
LinkedHashSet
LinkedHashSet维护插入顺序。
特点
| 特点 | 说明 |
|---|---|
| 有序 | 按插入顺序排列 |
| 不重复 | 自动去重 |
示例:LinkedHashSet
JAVA
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<>();
set.add("Charlie");
set.add("Alice");
set.add("Bob");
System.out.println("有序集合:" + set); // [Charlie, Alice, Bob]
}
}
集合运算
并集
JAVA
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3));
Set<Integer> set2 = new HashSet<>(Arrays.asList(3, 4, 5));
Set<Integer> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("并集:" + union); // [1, 2, 3, 4, 5]
交集
JAVA
Set<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("交集:" + intersection); // [3]
差集
JAVA
Set<Integer> diff = new HashSet<>(set1);
diff.removeAll(set2);
System.out.println("差集:" + diff); // [1, 2]
Set对比
| 特性 | HashSet | TreeSet | LinkedHashSet |
|---|---|---|---|
| 顺序 | 无序 | 排序 | 插入顺序 |
| 底层 | HashMap | 红黑树 | 链表+HashMap |
| 性能 | O(1) | O(log n) | O(1) |
| null | 允许1个 | 不允许 | 允许1个 |
选择建议
| 需求 | 选择 |
|---|---|
| 不关心顺序 | HashSet |
| 需要排序 | TreeSet |
| 保持插入顺序 | LinkedHashSet |
示例:去重练习
JAVA
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RemoveDuplicates {
// 方式1:Set去重
public static <T> List<T> removeDuplicates1(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
// 方式2:Stream去重
public static <T> List<T> removeDuplicates2(List<T> list) {
return list.stream().distinct().collect(Collectors.toList());
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Alice");
list.add("Bob");
list.add("Alice");
list.add("Charlie");
list.add("Bob");
System.out.println("原列表:" + list);
System.out.println("去重后:" + removeDuplicates1(list));
}
}
❓ 常见问题
Q Set和List怎么选?
A 需要去重用Set,需要有序且可重复用List。
Q TreeSet排序规则是什么?
A 默认自然排序(Comparable),可以自定义Comparator。
Q HashSet为什么快?
A 基于HashMap,通过hashCode直接定位,时间复杂度O(1)。
📖 小节
- Set不重复,HashSet无序,TreeSet排序,LinkedHashSet保序
- 去重依赖hashCode和equals方法
- 集合运算:并集(addAll)、交集(retainAll)、差集(removeAll)
📝 作业
- 去重: 将List中的重复元素去除,保持原顺序
- 交集: 找出两个Set的交集
- 排序: 用TreeSet对字符串按长度排序
下一课
下一课我们将学习 Map与HashMap,了解键值对集合。



