Java 中的 Set 集合:深度解析与代码示例

在 Java 集合框架(Java Collections Framework)中,Set 是一种非常重要的接口,它继承自 Collection 接口。与 ListQueue 不同,Set 不允许存储重复的元素,并且没有特定的顺序(除非使用 LinkedHashSetTreeSet 等特定实现)。本文将深入探讨 Set 的特性、主要实现类及其用法,并通过丰富的代码示例来展示其实际应用。

1. Set 接口的基本特性

  • 唯一性Set 中的元素是唯一的,不允许有重复的元素。
  • 无序性:默认情况下,Set 不保证元素的迭代顺序(除非使用了特定实现如 LinkedHashSet)。
  • 空集合:可以通过 Collections.emptySet() 创建一个不可变的空 Set 实例。

2. Set 的主要实现类

  • HashSet:基于哈希表实现,允许 null 元素,不保证迭代顺序。
  • LinkedHashSet:维护元素的插入顺序,允许 null 元素。
  • TreeSet:基于红黑树实现,元素按自然顺序或自定义比较器排序,不允许 null 元素。

3. HashSet 的使用

HashSet 是最常用的 Set 实现之一,它基于哈希表,因此提供了非常快的查找、添加和删除操作。

import java.util.HashSet;
import java.util.Set;
 
public class HashSetExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        
        set.add("Apple");
        set.add("Banana");
        set.add("Cherry");
        
        // 尝试添加重复元素
        set.add("Apple");
        
        // 输出集合内容
        System.out.println(set);  // 输出: [Apple, Banana, Cherry]
        
        // 检查元素是否存在
        boolean containsApple = set.contains("Apple");
        System.out.println("Contains Apple: " + containsApple);  // 输出: true
        
        // 遍历集合
        for (String fruit : set) {
            System.out.println(fruit);
        }
    }
}

4. LinkedHashSet 的使用

LinkedHashSet 保留了元素的插入顺序,这在需要保持元素顺序的场景下非常有用。

import java.util.LinkedHashSet;
import java.util.Set;
 
public class LinkedHashSetExample {
    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<>();
        
        set.add("One");
        set.add("Two");
        set.add("Three");
        
        // 添加重复元素(实际不会添加)
        set.add("One");
        
        // 输出集合内容,按插入顺序
        System.out.println(set);  // 输出: [One, Two, Three]
        
        // 遍历集合
        for (String item : set) {
            System.out.println(item);
        }
    }
}

5. TreeSet 的使用

TreeSet 基于红黑树实现,元素按自然顺序排序,或者根据提供的比较器排序。它不允许 null 元素。

import java.util.Set;
import java.util.TreeSet;
 
public class TreeSetExample {
    public static void main(String[] args) {
        Set<Integer> set = new TreeSet<>();
        
        set.add(5);
        set.add(1);
        set.add(10);
        set.add(3);
        
        // 输出集合内容,按自然顺序排序
        System.out.println(set);  // 输出: [1, 3, 5, 10]
        
        // 自定义比较器
        Set<String> customSet = new TreeSet<>((s1, s2) -> s2.compareTo(s1));
        customSet.add("Apple");
        customSet.add("Banana");
        customSet.add("Cherry");
        
        // 输出集合内容,按自定义比较器排序(降序)
        System.out.println(customSet);  // 输出: [Cherry, Banana, Apple]
        
        // 遍历集合
        for (String fruit : customSet) {
            System.out.println(fruit);
        }
    }
}

6. 常见的 Set 操作

Java 的 Set 接口还提供了许多有用的方法来操作集合,如:

  • addAll(Collection<? extends E> c):将指定集合中的所有元素添加到当前集合中(如果它们尚未存在)。
  • removeAll(Collection<?> c):从当前集合中移除指定集合中包含的所有元素。
  • retainAll(Collection<?> c):仅保留当前集合中也包含在指定集合中的元素。
  • clear():移除集合中的所有元素。
import java.util.HashSet;
import java.util.Set;
 
public class SetOperationsExample {
    public static void main(String[] args) {
        Set<String> set1 = new HashSet<>();
        set1.add("A");
        set1.add("B");
        set1.add("C");
        
        Set<String> set2 = new HashSet<>();
        set2.add("B");
        set2.add("C");
        set2.add("D");
        
        // 添加所有元素(不重复)
        set1.addAll(set2);
        System.out.println("After addAll: " + set1);  // 输出: [A, B, C, D]
        
        // 移除 set2 中的所有元素
        set1.removeAll(set2);
        System.out.println("After removeAll: " + set1);  // 输出: [A]
        
        // 保留 set2 中的元素
        set1.addAll(set2);  // 恢复到 [A, B, C, D]
        set1.retainAll(set2);
        System.out.println("After retainAll: " + set1);  // 输出: [B, C, D]
        
        // 清空集合
        set1.clear();
        System.out.println("After clear: " + set1);  // 输出: []
    }
}

总结

Set 是 Java 集合框架中一个强大且灵活的接口,通过不同的实现类,可以满足各种特定的需求。HashSet 提供了快速的查找和添加操作,LinkedHashSet 保留了元素的插入顺序,而 TreeSet 则提供了排序功能。掌握这些集合类的特性和用法,对于编写高效、可维护的 Java 程序至关重要。希望本文能帮助你深入理解 Set 的工作机制,并通过丰富的代码示例,更好地掌握其在实际开发中的应用。