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)。

📖 小节

📝 作业

  1. 去重: 将List中的重复元素去除,保持原顺序
  2. 交集: 找出两个Set的交集
  3. 排序: 用TreeSet对字符串按长度排序

下一课

下一课我们将学习 Map与HashMap,了解键值对集合。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏