Java集合-Set讲解 - 指南


Java 集合框架(Collections Framework)是 Java 中用于 存储和操作数据组的重要架构。它提供了一组接口、实现类和算法。

一、集合框架层次结构

Collection (接口)
├── List (接口 - 有序,可重复)
│   ├── ArrayList (实现类)
│   ├── LinkedList (实现类)
│   ├── Vector (线程安全,已过时)
│   └── Stack (继承Vector)
│
├── Set (接口 - 无序,不可重复)
│   ├── HashSet (实现类)
│   │   └── LinkedHashSet (保持插入顺序)
│   ├── SortedSet (接口)
│   │   └── TreeSet (实现类)
│   └── EnumSet (专用于枚举)
│
└── Queue (接口 - 队列)
    ├── Deque (双端队列接口)
    │   ├── ArrayDeque (实现类)
    │   └── LinkedList (也实现了Deque)
    │
    ├── PriorityQueue (优先队列)
    └── BlockingQueue (阻塞队列接口)
        ├── ArrayBlockingQueue
        ├── LinkedBlockingQueue
        └── PriorityBlockingQueue
Map (接口 - 键值对)
├── HashMap (实现类)
│   └── LinkedHashMap (保持插入顺序)
├── TreeMap (基于红黑树)
├── Hashtable (线程安全,已过时)
├── WeakHashMap (弱引用)
└── ConcurrentHashMap (并发版)

Java集合大致可以分为两大体系,一个是Collection,另一个是Map

  • Collection :主要由ListSetQueue接口组成,List代表有序、重复的集合;其中Set代表无序、不可重复的集合;Queue体系集合,代表一种队列集合实现。
  • Map:则代表具有映射关系的键值对集合。
    java.util.Collection下的接口和继承类关系简易结构图:
    在这里插入图片描述
    java.util.Map下的接口和继承类关系简易结构图:

在这里插入图片描述
其中,Java 集合框架中主要封装的是典型的数据结构和算法,如动态数组、双向链表、队列、栈、Set、Map 等。

二、Collection集合

通过集合的关系图我们可以知道Collection是集合的顶层父类,他定义了集合的基本方法如

基本操作方法

方法签名功能描述返回值示例时间复杂度
int size()返回集合中元素的数量元素个数list.size()3O(1)
boolean isEmpty()判断集合是否为空true/falselist.isEmpty()falseO(1)
boolean contains(Object o)判断是否包含指定元素true/falselist.contains("A")trueList: O(n)
Set: O(1)
TreeSet: O(log n)
boolean add(E e)添加元素到集合是否成功list.add("D")trueArrayList: 均摊O(1)
LinkedList: O(1)
TreeSet: O(log n)
boolean remove(Object o)移除指定元素是否成功list.remove("A")trueArrayList: O(n)
LinkedList: O(n)
HashSet: O(1)

批量操作方法

方法签名功能描述返回值示例说明
boolean containsAll(Collection<?> c)是否包含集合c中所有元素true/falselist.containsAll(subList)检查子集关系
boolean addAll(Collection<? extends E> c)添加集合c中所有元素是否改变list.addAll(anotherList)批量添加
boolean removeAll(Collection<?> c)移除集合c中所有元素是否改变list.removeAll(toRemove)差集操作
boolean retainAll(Collection<?> c)仅保留集合c中元素是否改变list.retainAll(common)交集操作
void clear()清空集合所有元素list.clear()集合变为空

转换和迭代方法

方法签名功能描述返回值示例说明
Object[] toArray()转换为Object数组Object数组list.toArray()返回新数组
<T> T[] toArray(T[] a)转换为指定类型数组指定类型数组list.toArray(new String[0])类型安全转换
Iterator<E> iterator()返回迭代器Iterator对象list.iterator()用于遍历集合
default boolean removeIf(Predicate<? super E> filter)条件删除是否改变list.removeIf(s -> s.length() > 3)Java 8+
default Spliterator<E> spliterator()返回分割迭代器Spliterator对象list.spliterator()Java 8+,并行遍历
default Stream<E> stream()返回顺序流Stream对象list.stream()Java 8+,流操作
default Stream<E> parallelStream()返回并行流Stream对象list.parallelStream()Java 8+,并行流操作

集合运算方法

方法数学运算示意图示例
addAll()并集A ∪ BA.addAll(B)
retainAll()交集A ∩ BA.retainAll(B)
removeAll()差集A - BA.removeAll(B)

常见操作示例

操作需求代码示例说明
遍历集合for (E e : collection) { ... }增强for循环
安全遍历并删除iterator.remove()使用迭代器删除
转换为数组String[] arr = coll.toArray(new String[0])推荐写法
批量添加元素coll.addAll(Arrays.asList("A","B","C"))初始化集合
过滤集合coll.removeIf(e -> e.startsWith("A"))Java 8+
集合判空if (!coll.isEmpty()) { ... }优于size() > 0

注意事项表格

方法注意事项推荐做法
contains()依赖equals()hashCode()正确实现这两个方法
remove(Object)只删除第一个匹配项使用removeIf()删除所有
toArray()无参方法返回Object[]使用带参方法指定类型
addAll()可能修改原集合注意并发修改异常
clear()不释放元素引用大集合考虑设为null
iterator()遍历时不能修改集合使用迭代器的remove()
场景建议理由
频繁包含检查使用HashSetO(1)时间复杂度
频繁插入删除使用LinkedList首尾操作O(1)
随机访问使用ArrayListO(1)索引访问
需要排序使用TreeSet自动维护顺序
线程安全使用并发集合ConcurrentHashMap
只读操作使用不可变集合Collections.unmodifiableXXX()

Collection集合中所包含的方法:

public interface Collection<E> extends Iterable<E> {
  // 基本操作方法
  int size();
  boolean isEmpty();
  boolean contains(Object o);
  boolean add(E e);
  boolean remove(Object o);
  // 批量操作
  boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
      boolean removeAll(Collection<?> c);
        boolean retainAll(Collection<?> c);
          void clear();
          // 数组转换
          Object[] toArray();
          <T> T[] toArray(T[] a);
            // 迭代器
            Iterator<E> iterator();
              // Java 8 新增方法
              default boolean removeIf(Predicate<? super E> filter) { ... }
                default Spliterator<E> spliterator() { ... }
                  default Stream<E> stream() { ... }
                    default Stream<E> parallelStream() { ... }
                      }

1、Set集合

Set集合的特点:元素不重复,存取无序,无下标;
在这里插入图片描述

Set (接口)
├── HashSet (实现类)
│   └── LinkedHashSet (子类)
├── SortedSet (接口)
│   └── TreeSet (实现类)
├── EnumSet (实现类)
└── CopyOnWriteArraySet (并发实现)

Set 接口核心特性

特性说明
唯一性不允许重复元素
无序性不保证插入顺序(除LinkedHashSet)
允许null大多数实现允许一个null元素
数学运算支持并集、交集、差集等操作
无索引不能通过索引访问元素

1、HashSet

一个标准的Set集合底层是使用HashMapKey实现的,特点为:线程不安全,不可重复,无序
HashSetJava 中最常用的 Set 实现,基于 HashMap 实现,具有快速查找、插入和删除的特性。

底层实现原理

public class HashSet<E> extends AbstractSet<E>
  implements Set<E>, Cloneable, java.io.Serializable {
    // 底层使用 HashMap 存储
    private transient HashMap<E, Object> map;
      // 所有元素共享的虚拟值
      private static final Object PRESENT = new Object();
      // 构造方法
      public HashSet() {
      map = new HashMap<>();  // 默认初始容量16,加载因子0.75
        }
        public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
          }
          public HashSet(int initialCapacity, float loadFactor) {
          map = new HashMap<>(initialCapacity, loadFactor);
            }
            }

核心特性

特性说明
底层实现基于 HashMap(元素作为Key)
唯一性不允许重复元素
无序性不保证插入顺序
允许null允许一个null元素
线程安全非线程安全
性能O(1) 的基本操作(平均情况)
扩容默认初始容量16,加载因子0.75

构造方法详解

// 1. 默认构造(容量16,加载因子0.75)
HashSet<String> set1 = new HashSet<>();
  // 2. 指定初始容量
  HashSet<String> set2 = new HashSet<>(100);  // 初始容量100
    // 3. 指定初始容量和加载因子
    HashSet<String> set3 = new HashSet<>(100, 0.8f);
      // 初始容量100,加载因子0.8
      // 4. 从集合构造
      List<String> list = Arrays.asList("A", "B", "C", "A");
        HashSet<String> set4 = new HashSet<>(list);  // 自动去重:[A, B, C]
          // 5. 特殊构造(包访问权限,用于LinkedHashSet)
          // HashSet(int initialCapacity, float loadFactor, boolean dummy)

添加元素

// HashSet的add方法实际调用HashMap的put方法
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
// HashMap.put() 方法的核心逻辑:
// 1. 计算key的hash值
// 2. 找到对应的桶(bucket)
// 3. 如果桶为空,直接插入
// 4. 如果桶不为空,遍历链表/红黑树
// 5. 找到相同key(equals为true)则替换值
// 6. 未找到则添加到链表/树末尾

删除元素

public boolean remove(Object o) {
return map.remove(o) == PRESENT;
}
// 实际调用HashMap.remove()

查找元素

public boolean contains(Object o) {
return map.containsKey(o);
}

基本操作

// 创建HashSet
HashSet<String> fruits = new HashSet<>();
  // 1. 添加元素
  fruits.add("Apple");
  fruits.add("Banana");
  fruits.add("Orange");
  fruits.add("Apple");  // 重复元素,不会添加
  fruits.add(null);     // 允许null
  System.out.println(fruits);  // [null, Apple, Banana, Orange]
  // 2. 检查元素
  boolean hasApple = fruits.contains("Apple");  // true
  boolean hasGrape = fruits.contains("Grape");  // false
  boolean isEmpty = fruits.isEmpty();           // false
  // 3. 删除元素
  fruits.remove("Banana");     // 删除成功返回true
  fruits.remove("Watermelon"); // 元素不存在返回false
  fruits.remove(null);         // 删除null元素
  // 4. 获取大小
  int size = fruits.size();    // 2
  // 5. 清空集合
  fruits.clear();
  System.out.println(fruits);  // []

遍历方式

HashSet<String> set = new HashSet<>();
  set.addAll(Arrays.asList("A", "B", "C", "D", "E"));
  // 1. 增强for循环
  for (String item : set) {
  System.out.println(item);
  }
  // 2. 迭代器
  Iterator<String> iterator = set.iterator();
    while (iterator.hasNext()) {
    String item = iterator.next();
    System.out.println(item);
    // iterator.remove(); // 可以在迭代时删除
    }
    // 3. Java 8+ forEach
    set.forEach(System.out::println);
    // 4. 转换为数组遍历
    Object[] array = set.toArray();
    for (Object obj : array) {
    System.out.println(obj);
    }
    // 5. 转换为流处理
    set.stream()
    .filter(s -> s.startsWith("A"))
    .forEach(System.out::println);

集合运算

HashSet<Integer> setA = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
  HashSet<Integer> setB = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
    // 1. 并集
    HashSet<Integer> union = new HashSet<>(setA);
      union.addAll(setB);
      System.out.println("并集: " + union);  // [1, 2, 3, 4, 5, 6, 7, 8]
      // 2. 交集
      HashSet<Integer> intersection = new HashSet<>(setA);
        intersection.retainAll(setB);
        System.out.println("交集: " + intersection);  // [4, 5]
        // 3. 差集 (A - B)
        HashSet<Integer> difference = new HashSet<>(setA);
          difference.removeAll(setB);
          System.out.println("差集A-B: " + difference);  // [1, 2, 3]
          // 4. 对称差集 (A ∪ B - A ∩ B)
          HashSet<Integer> symmetricDiff = new HashSet<>(setA);
            symmetricDiff.addAll(setB);  // 先并集
            HashSet<Integer> tmp = new HashSet<>(setA);
              tmp.retainAll(setB);         // 交集
              symmetricDiff.removeAll(tmp); // 减去交集
              System.out.println("对称差集: " + symmetricDiff);  // [1, 2, 3, 6, 7, 8]
              // 5. 子集判断
              boolean isSubset = setA.containsAll(new HashSet<>(Arrays.asList(1, 2)));  // true
                boolean isSuperset = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6)).containsAll(setA);  // true

2、LinkedHashSet

LinkedHashSetHashSet 的子类,在 HashSet 的基础上维护了元素的插入顺序,通过双向链表记录插入顺序。
底层实现原理

public class LinkedHashSet<E> extends HashSet<E>
  implements Set<E>, Cloneable, java.io.Serializable {
    // 继承自HashSet,底层使用LinkedHashMap
    // 构造方法调用父类的特殊构造
    public LinkedHashSet() {
    super(16, 0.75f, true);  // 关键:dummy参数为true
    }
    }
    // HashSet中的特殊构造方法
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
      }

LinkedHashMap 结构

public class LinkedHashMap<K,V> extends HashMap<K,V> {
  // 双向链表节点
  static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;  // 前后指针
      Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
        }
        }
        // 链表头尾
        transient LinkedHashMap.Entry<K,V> head;
          transient LinkedHashMap.Entry<K,V> tail;
            // 访问顺序标志
            final boolean accessOrder;
            }

核心特性

特性LinkedHashSetHashSetTreeSet
底层实现LinkedHashMapHashMapTreeMap
唯一性✅ 不允许重复✅ 不允许重复✅ 不允许重复
顺序性✅ 插入顺序❌ 无序✅ 自然排序
允许null✅ 允许一个null✅ 允许一个null❌ 不允许null
线程安全❌ 非线程安全❌ 非线程安全❌ 非线程安全
性能O(1) 基本操作O(1) 基本操作O(log n) 基本操作
内存开销较高(维护链表)较低中等

构造方法详解

// 1. 默认构造(容量16,加载因子0.75)
LinkedHashSet<String> set1 = new LinkedHashSet<>();
  // 2. 指定初始容量
  LinkedHashSet<String> set2 = new LinkedHashSet<>(100);
    // 3. 指定初始容量和加载因子
    LinkedHashSet<String> set3 = new LinkedHashSet<>(100, 0.8f);
      // 4. 从集合构造(保持原集合的顺序)
      List<String> list = Arrays.asList("C", "A", "B", "C", "A");
        LinkedHashSet<String> set4 = new LinkedHashSet<>(list);
          // 顺序:[C, A, B](去重,保持第一次出现的顺序)
          // 特殊:通过Collections.newSetFromMap()
          Set<String> synchronizedSet = Collections.newSetFromMap(
            new LinkedHashMap<String, Boolean>()
              );

基本操作

// 创建LinkedHashSet
LinkedHashSet<String> fruits = new LinkedHashSet<>();
  // 1. 添加元素(保持插入顺序)
  fruits.add("Apple");
  fruits.add("Banana");
  fruits.add("Orange");
  fruits.add("Apple");      // 重复,不添加
  fruits.add("Grape");
  fruits.add(null);         // 允许null
  System.out.println(fruits);
  // 输出:[Apple, Banana, Orange, Grape, null](保持插入顺序)
  // 2. 遍历验证顺序
  System.out.println("遍历顺序:");
  for (String fruit : fruits) {
  System.out.println(fruit);  // Apple, Banana, Orange, Grape, null
  }
  // 3. 删除元素(不会影响其他元素的顺序)
  fruits.remove("Banana");
  System.out.println("删除Banana后:" + fruits);
  // [Apple, Orange, Grape, null]
  // 4. 插入新元素(添加到末尾)
  fruits.add("Peach");
  System.out.println("添加Peach后:" + fruits);
  // [Apple, Orange, Grape, null, Peach]
  // 5. 重新添加已存在元素(位置不变)
  fruits.add("Apple");  // Apple已存在,位置不变
  System.out.println("重新添加Apple后:" + fruits);
  // [Apple, Orange, Grape, null, Peach](顺序不变)

迭代器特性

LinkedHashSet<Integer> set = new LinkedHashSet<>();
  for (int i = 1; i <= 5; i++) {
  set.add(i);
  }
  // 迭代器按插入顺序遍历
  Iterator<Integer> iterator = set.iterator();
    System.out.print("迭代器输出:");
    while (iterator.hasNext()) {
    System.out.print(iterator.next() + " ");  // 1 2 3 4 5
    }
    System.out.println();
    // 列表迭代器(LinkedHashSet没有直接提供,但可以通过转换)
    List<Integer> list = new ArrayList<>(set);
      ListIterator<Integer> listIterator = list.listIterator();
        while (listIterator.hasNext()) {
        System.out.print(listIterator.next() + " ");
        }

集合运算保持顺序

LinkedHashSet<Integer> setA = new LinkedHashSet<>();
  Collections.addAll(setA, 1, 3, 5, 7, 9);
  LinkedHashSet<Integer> setB = new LinkedHashSet<>();
    Collections.addAll(setB, 2, 4, 6, 8, 5, 7);
    // 并集(保持setA和setB的插入顺序)
    LinkedHashSet<Integer> union = new LinkedHashSet<>(setA);
      union.addAll(setB);  // setB的元素按setB的顺序添加到末尾
      System.out.println("并集:" + union);  // [1, 3, 5, 7, 9, 2, 4, 6, 8]
      // 交集(保持setA的顺序)
      LinkedHashSet<Integer> intersection = new LinkedHashSet<>(setA);
        intersection.retainAll(setB);
        System.out.println("交集:" + intersection);  // [5, 7](保持setA中的顺序)
        // 差集(保持setA的顺序)
        LinkedHashSet<Integer> difference = new LinkedHashSet<>(setA);
          difference.removeAll(setB);
          System.out.println("差集:" + difference);  // [1, 3, 9](保持setA中的顺序)

3、TreeSet

TreeSet 是基于红黑树(Red-Black Tree) 实现的 NavigableSet,元素自动排序不允许重复
底层实现原理

public class TreeSet<E> extends AbstractSet<E>
  implements NavigableSet<E>, Cloneable, java.io.Serializable {
    // 底层使用 TreeMap
    private transient NavigableMap<E,Object> m;
      // 虚拟值
      private static final Object PRESENT = new Object();
      // 构造方法
      public TreeSet() {
      this(new TreeMap<E,Object>());  // 自然排序
        }
        public TreeSet(Comparator<? super E> comparator) {
          this(new TreeMap<>(comparator));  // 定制排序
            }
            }

红黑树特性

// TreeMap 中的红黑树节点
static final class Entry<K,V> implements Map.Entry<K,V> {
  K key;
  V value;
  Entry<K,V> left;    // 左子树
    Entry<K,V> right;   // 右子树
      Entry<K,V> parent;  // 父节点
        boolean color = BLACK;  // 颜色(红/黑)
        // 红黑树五大特性:
        // 1. 节点是红色或黑色
        // 2. 根节点是黑色
        // 3. 所有叶子节点(NIL)是黑色
        // 4. 红色节点的两个子节点都是黑色
        // 5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
        }

核心特性

特性TreeSetHashSetLinkedHashSet
底层结构红黑树哈希表哈希表+链表
排序方式自然排序/定制排序无顺序插入顺序
时间复杂度O(log n)O(1)O(1)
允许null❌(除非指定Comparator)
线程安全
内存开销较高较低中等
范围查询✅ 支持

构造方法详解

// 1. 默认构造(自然排序)
TreeSet<Integer> set1 = new TreeSet<>();
  // 元素必须实现Comparable接口
  // 2. 指定Comparator
  TreeSet<String> set2 = new TreeSet<>(Comparator.reverseOrder());
    // 3. 从集合构造(使用自然排序)
    List<Integer> list = Arrays.asList(5, 2, 8, 1, 9);
      TreeSet<Integer> set3 = new TreeSet<>(list);
        // 自动排序:[1, 2, 5, 8, 9]
        // 4. 从SortedSet构造(保持原有排序)
        SortedSet<Integer> sorted = new TreeSet<>(Comparator.reverseOrder());
          sorted.addAll(Arrays.asList(5, 2, 8));
          TreeSet<Integer> set4 = new TreeSet<>(sorted);
            // 保持逆序:[8, 5, 2]
            // 5. 使用NavigableMap构造
            NavigableMap<String, Object> map = new TreeMap<>();
              TreeSet<String> set5 = new TreeSet<>(map);

元素排序规则
自然排序(Comparable)

// 元素类必须实现Comparable接口
class Student implements Comparable<Student> {
  private String name;
  private int score;
  public Student(String name, int score) {
  this.name = name;
  this.score = score;
  }
  @Override
  public int compareTo(Student other) {
  // 先按分数排序,分数相同按姓名排序
  int scoreCompare = Integer.compare(this.score, other.score);
  if (scoreCompare != 0) {
  return scoreCompare;
  }
  return this.name.compareTo(other.name);
  }
  @Override
  public String toString() {
  return name + ":" + score;
  }
  }
  // 使用
  TreeSet<Student> students = new TreeSet<>();
    students.add(new Student("Alice", 85));
    students.add(new Student("Bob", 92));
    students.add(new Student("Charlie", 78));
    students.add(new Student("David", 85)); // 分数相同,按姓名排序
    System.out.println(students);
    // [Charlie:78, Alice:85, David:85, Bob:92]

定制排序(Comparator)

// 1. 字符串长度排序
TreeSet<String> byLength = new TreeSet<>(
  Comparator.comparing(String::length)
  .thenComparing(String::compareTo)
  );
  byLength.addAll(Arrays.asList("Apple", "Banana", "Cat", "Dog", "Elephant"));
  System.out.println(byLength); // [Cat, Dog, Apple, Banana, Elephant]
  // 2. 逆序排序
  TreeSet<Integer> reversed = new TreeSet<>(Comparator.reverseOrder());
    reversed.addAll(Arrays.asList(5, 2, 8, 1, 9));
    System.out.println(reversed); // [9, 8, 5, 2, 1]
    // 3. 复杂对象多字段排序
    class Product {
    String name;
    double price;
    int stock;
    }
    TreeSet<Product> products = new TreeSet<>(
      Comparator.comparing(Product::getPrice)
      .thenComparing(Product::getName)
      .thenComparingInt(Product::getStock)
      );
      // 4. 处理null值
      TreeSet<String> withNulls = new TreeSet<>(
        Comparator.nullsFirst(String::compareTo)
        );
        withNulls.add(null);
        withNulls.add("Apple");
        withNulls.add("Banana");
        System.out.println(withNulls); // [null, Apple, Banana]

基本操作

// 创建TreeSet
TreeSet<String> fruits = new TreeSet<>();
  // 1. 添加元素(自动排序)
  fruits.add("Orange");
  fruits.add("Apple");
  fruits.add("Banana");
  fruits.add("Apple");  // 重复元素,不会添加
  // fruits.add(null);  // 抛出NullPointerException(默认情况下)
  System.out.println(fruits);  // [Apple, Banana, Orange]
  // 2. 遍历(按排序顺序)
  for (String fruit : fruits) {
  System.out.println(fruit);  // Apple, Banana, Orange
  }
  // 3. 删除元素
  fruits.remove("Banana");
  System.out.println(fruits);  // [Apple, Orange]
  // 4. 清空集合
  fruits.clear();
  System.out.println(fruits.isEmpty());  // true

范围查询操作

TreeSet<Integer> numbers = new TreeSet<>();
  for (int i = 1; i <= 10; i++) {
  numbers.add(i);
  }
  // 子集操作
  SortedSet<Integer> headSet = numbers.headSet(5);      // [1, 2, 3, 4]
    SortedSet<Integer> tailSet = numbers.tailSet(6);      // [6, 7, 8, 9, 10]
      SortedSet<Integer> subSet = numbers.subSet(3, 7);     // [3, 4, 5, 6]
        // 包含边界控制
        SortedSet<Integer> headSetInclusive = numbers.headSet(5, true);   // [1, 2, 3, 4, 5]
          SortedSet<Integer> subSetExclusive = numbers.subSet(3, false, 7, false); // [4, 5, 6]
            // 获取范围内的元素(闭区间)
            NavigableSet<Integer> range = numbers.subSet(2, true, 8, true);
              System.out.println("2到8的范围: " + range);  // [2, 3, 4, 5, 6, 7, 8]

导航方法

TreeSet<Integer> set = new TreeSet<>(Arrays.asList(2, 4, 6, 8, 10));
  // 获取首尾元素
  Integer first = set.first();      // 2
  Integer last = set.last();        // 10
  // 小于/小于等于
  Integer lower = set.lower(5);     // 4(小于5的最大元素)
  Integer floor = set.floor(5);     // 4(小于等于5的最大元素)
  Integer floorExact = set.floor(4); // 4
  // 大于/大于等于
  Integer higher = set.higher(5);   // 6(大于5的最小元素)
  Integer ceiling = set.ceiling(5); // 6(大于等于5的最小元素)
  Integer ceilingExact = set.ceiling(4); // 4
  // 弹出首尾元素
  Integer pollFirst = set.pollFirst();  // 2,并从集合移除
  Integer pollLast = set.pollLast();    // 10,并从集合移除
  System.out.println("弹出后: " + set);  // [4, 6, 8]

4、ConcurrentSkipListSet

ConcurrentSkipListSet 是基于跳表(Skip List)实现的线程安全的有序集合,是 TreeSet 的并发版本
底层实现原理
跳表数据结构

// 跳表节点结构
static final class Node<K,V> {
  final K key;
  volatile Object value;
  volatile Node<K,V> next;
    // 索引层
    volatile Index<K,V>[] indices;
      }
      // 索引层结构
      static class Index<K,V> {
        final Node<K,V> node;      // 引用的数据节点
          final Index<K,V> down;     // 下层索引
            volatile Index<K,V> right; // 右侧索引
              Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
                this.node = node;
                this.down = down;
                this.right = right;
                }
                }

跳表示意图

Level 3:  head ------------------------> 50 ------------------------> tail
           ↓                              ↓                            ↓
Level 2:  head ------------> 30 ------------> 50 ------------> 70 ---> tail
           ↓                  ↓                ↓                ↓        ↓
Level 1:  head ----> 10 ----> 30 ----> 40 ----> 50 ----> 60 ----> 70 ---> tail
           ↓          ↓        ↓        ↓        ↓        ↓        ↓        ↓
Level 0:  head -> 10 -> 20 -> 30 -> 40 -> 50 -> 60 -> 70 -> 80 -> 90 -> tail

核心特性

特性ConcurrentSkipListSetTreeSetCopyOnWriteArraySet
底层结构跳表(Skip List)红黑树动态数组
线程安全✅ 是(无锁CAS)❌ 否✅ 是
有序性✅ 自然排序✅ 自然排序❌ 无序
并发性能✅ 高(读多写多)❌ 低✅ 读多写少
时间复杂度O(log n)O(log n)读O(1),写O(n)
允许null❌ 不允许❌ 不允许✅ 允许
内存开销中等较高

构造方法

// 1. 默认构造(自然排序)
ConcurrentSkipListSet<Integer> set1 = new ConcurrentSkipListSet<>();
  // 2. 指定Comparator
  ConcurrentSkipListSet<String> set2 =
    new ConcurrentSkipListSet<>(Comparator.reverseOrder());
      // 3. 从集合构造
      List<Integer> list = Arrays.asList(5, 2, 8, 1, 9);
        ConcurrentSkipListSet<Integer> set3 =
          new ConcurrentSkipListSet<>(list);
            // 自动排序:[1, 2, 5, 8, 9]
            // 4. 从SortedSet构造
            SortedSet<Integer> sorted = new TreeSet<>();
              sorted.addAll(Arrays.asList(5, 2, 8));
              ConcurrentSkipListSet<Integer> set4 =
                new ConcurrentSkipListSet<>(sorted);

基本操作

ConcurrentSkipListSet<String> set = new ConcurrentSkipListSet<>();
  // 1. 并发添加元素
  Thread t1 = new Thread(() -> {
  set.add("Apple");
  set.add("Banana");
  });
  Thread t2 = new Thread(() -> {
  set.add("Orange");
  set.add("Apple");  // 重复元素,不会添加
  });
  t1.start();
  t2.start();
  t1.join();
  t2.join();
  System.out.println(set);  // [Apple, Banana, Orange](已排序)
  // 2. 安全遍历
  for (String fruit : set) {
  System.out.println(fruit);  // 线程安全遍历
  }
  // 3. 删除元素
  boolean removed = set.remove("Banana");
  System.out.println("删除Banana: " + removed);  // true
  // 4. 清空集合
  set.clear();
  System.out.println("是否为空: " + set.isEmpty());  // true

并发操作示例

public class ConcurrentSetExample {
private final ConcurrentSkipListSet<Integer> numbers =
  new ConcurrentSkipListSet<>();
    private final int THREAD_COUNT = 10;
    private final int OPERATIONS_PER_THREAD = 1000;
    public void concurrentTest() throws InterruptedException {
    List<Thread> threads = new ArrayList<>();
      // 创建生产者线程
      for (int i = 0; i < THREAD_COUNT; i++) {
      final int threadId = i;
      Thread producer = new Thread(() -> {
      Random random = new Random();
      for (int j = 0; j < OPERATIONS_PER_THREAD; j++) {
      int num = random.nextInt(10000);
      numbers.add(num);  // 并发添加
      if (j % 100 == 0) {
      // 偶尔删除
      Integer first = numbers.pollFirst();
      if (first != null) {
      // 处理删除的元素
      }
      }
      }
      });
      threads.add(producer);
      }
      // 创建消费者线程(只读)
      for (int i = 0; i < THREAD_COUNT / 2; i++) {
      Thread consumer = new Thread(() -> {
      for (int j = 0; j < OPERATIONS_PER_THREAD; j++) {
      // 并发遍历(安全)
      for (Integer num : numbers) {
      // 处理元素
      }
      // 范围查询
      Set<Integer> subset = numbers.subSet(1000, 2000);
        // 处理子集
        }
        });
        threads.add(consumer);
        }
        // 启动所有线程
        threads.forEach(Thread::start);
        // 等待所有线程完成
        for (Thread thread : threads) {
        thread.join();
        }
        System.out.println("最终集合大小: " + numbers.size());
        System.out.println("最小值: " + numbers.first());
        System.out.println("最大值: " + numbers.last());
        }
        }

导航和范围查询

ConcurrentSkipListSet<Integer> set = new ConcurrentSkipListSet<>();
  for (int i = 1; i <= 100; i++) {
  set.add(i);
  }
  // 1. 导航方法(线程安全)
  Integer lower = set.lower(50);      // 49(小于50的最大元素)
  Integer floor = set.floor(50);      // 50(小于等于50的最大元素)
  Integer higher = set.higher(50);    // 51(大于50的最小元素)
  Integer ceiling = set.ceiling(50);  // 50(大于等于50的最小元素)
  // 2. 弹出首尾元素(线程安全)
  Integer first = set.pollFirst();    // 1,并从集合移除
  Integer last = set.pollLast();      // 100,并从集合移除
  // 3. 子集操作(返回视图,支持并发修改)
  ConcurrentNavigableSet<Integer> headSet = set.headSet(50);      // [2..49]
    ConcurrentNavigableSet<Integer> tailSet = set.tailSet(51);      // [51..99]
      ConcurrentNavigableSet<Integer> subSet = set.subSet(20, 80);    // [20..79]
        // 4. 包含边界的子集
        ConcurrentNavigableSet<Integer> inclusiveSubSet =
          set.subSet(20, true, 80, true);  // [20..80]
          // 5. 逆序视图
          ConcurrentNavigableSet<Integer> descendingSet = set.descendingSet();
            // 逆序遍历
            Iterator<Integer> descendingIterator = set.descendingIterator();

5、CopyOnWriteArraySet

CopyOnWriteArraySet 是基于 CopyOnWriteArrayList 实现的线程安全的 Set,采用 “写时复制” 策略,适合读多写少的并发场景。

底层实现原理

public class CopyOnWriteArraySet<E> extends AbstractSet<E>
  implements java.io.Serializable {
  // 底层使用 CopyOnWriteArrayList
  private final CopyOnWriteArrayList<E> al;
    // 构造方法
    public CopyOnWriteArraySet() {
    al = new CopyOnWriteArrayList<E>();
      }
      public CopyOnWriteArraySet(Collection<? extends E> c) {
        // 使用CopyOnWriteArrayList的去重构造
        al = new CopyOnWriteArrayList<E>();
          al.addAllAbsent(c);  // 只添加不存在的元素
          }
          }

核心特性

特性CopyOnWriteArraySetHashSetConcurrentSkipListSet
底层结构动态数组哈希表跳表
线程安全✅ 是❌ 否✅ 是
写时复制✅ 是❌ 否❌ 否
有序性插入顺序无序自然排序
允许null✅ 允许✅ 允许❌ 不允许
迭代器快照迭代器快速失败弱一致迭代器
读性能✅ 极快(无锁)✅ 快✅ 快
写性能❌ 慢(复制数组)✅ 快✅ 较快
内存开销中等
构造方法
// 1. 默认构造(空集合)
CopyOnWriteArraySet<String> set1 = new CopyOnWriteArraySet<>();
  // 2. 从集合构造(自动去重)
  List<String> list = Arrays.asList("A", "B", "A", "C");
    CopyOnWriteArraySet<String> set2 = new CopyOnWriteArraySet<>(list);
      // 结果:[A, B, C](保持第一次出现的顺序)
      // 3. 从数组构造(需要先转为集合)
      String[] array = {"X", "Y", "X", "Z"};
      CopyOnWriteArraySet<String> set3 =
        new CopyOnWriteArraySet<>(Arrays.asList(array));
          // 4. 从其他Set构造(保持原Set特性)
          Set<String> hashSet = new HashSet<>(Arrays.asList("A", "B", "C"));
            CopyOnWriteArraySet<String> set4 = new CopyOnWriteArraySet<>(hashSet);

添加元素

// CopyOnWriteArraySet的add方法
public boolean add(E e) {
return al.addIfAbsent(e);  // 关键:保证元素唯一
}
// CopyOnWriteArrayList的addIfAbsent实现
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
// 检查是否已存在
if (indexOf(e, snapshot, 0, snapshot.length) >= 0) {
return false;
}
// 不存在,创建新数组并添加
return addIfAbsent(e, snapshot);
}
private boolean addIfAbsent(E e, Object[] snapshot) {
synchronized (lock) {
Object[] current = getArray();
int len = current.length;
// 再次检查(双重检查锁定模式)
if (snapshot != current) {
int common = Math.min(snapshot.length, len);
for (int i = 0; i < common; i++) {
if (current[i] != snapshot[i] && eq(e, current[i])) {
return false;
}
}
if (indexOf(e, current, common, len) >= 0) {
return false;
}
}
// 创建新数组并添加元素
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
}
}

查找元素

// contains方法(无锁读取)
public boolean contains(Object o) {
return al.contains(o);  // 直接读取当前数组
}
// CopyOnWriteArrayList.contains实现
public boolean contains(Object o) {
Object[] elements = getArray();
return indexOf(o, elements, 0, elements.length) >= 0;
}

迭代器

// 返回快照迭代器
public Iterator<E> iterator() {
  return al.iterator();  // 基于创建时的数组快照
  }
  // CopyOnWriteArrayList.iterator实现
  public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
      }
      static final class COWIterator<E> implements ListIterator<E> {
        private final Object[] snapshot;
        private int cursor;
        COWIterator(Object[] elements, int initialCursor) {
        cursor = initialCursor;
        snapshot = elements;  // 保存数组快照
        }
        public boolean hasNext() {
        return cursor < snapshot.length;
        }
        public E next() {
        if (!hasNext()) throw new NoSuchElementException();
        return (E) snapshot[cursor++];
        }
        // 不支持修改操作
        public void remove() {
        throw new UnsupportedOperationException();
        }
        }

基本操作

// 创建CopyOnWriteArraySet
CopyOnWriteArraySet<String> userSet = new CopyOnWriteArraySet<>();
  // 1. 添加元素(线程安全)
  userSet.add("Alice");
  userSet.add("Bob");
  userSet.add("Alice");  // 重复,不会添加
  userSet.add(null);     // 允许null
  System.out.println(userSet);  // [Alice, Bob, null]
  // 2. 并发读取(无需加锁)
  boolean hasAlice = userSet.contains("Alice");  // true(无锁读取)
  int size = userSet.size();                     // 3(无锁读取)
  // 3. 安全遍历
  for (String user : userSet) {
  System.out.println(user);  // 不会抛出ConcurrentModificationException
  }
  // 4. 删除元素
  boolean removed = userSet.remove("Bob");  // true
  userSet.remove(null);                     // 删除null元素
  // 5. 批量操作
  userSet.addAll(Arrays.asList("Charlie", "David", "Eve"));
  userSet.removeAll(Arrays.asList("Alice", "Charlie"));
  System.out.println("最终集合: " + userSet);  // [David, Eve]

集合运算

CopyOnWriteArraySet<Integer> setA = new CopyOnWriteArraySet<>(
  Arrays.asList(1, 2, 3, 4, 5)
  );
  CopyOnWriteArraySet<Integer> setB = new CopyOnWriteArraySet<>(
    Arrays.asList(4, 5, 6, 7, 8)
    );
    // 1. 并集(线程安全)
    CopyOnWriteArraySet<Integer> union = new CopyOnWriteArraySet<>(setA);
      union.addAll(setB);
      System.out.println("并集: " + union);  // [1, 2, 3, 4, 5, 6, 7, 8]
      // 2. 交集
      CopyOnWriteArraySet<Integer> intersection = new CopyOnWriteArraySet<>(setA);
        intersection.retainAll(setB);
        System.out.println("交集: " + intersection);  // [4, 5]
        // 3. 差集
        CopyOnWriteArraySet<Integer> difference = new CopyOnWriteArraySet<>(setA);
          difference.removeAll(setB);
          System.out.println("差集(A-B): " + difference);  // [1, 2, 3]
          // 4. 对称差集
          CopyOnWriteArraySet<Integer> symmetricDiff = new CopyOnWriteArraySet<>(union);
            symmetricDiff.removeAll(intersection);
            System.out.println("对称差集: " + symmetricDiff);  // [1, 2, 3, 6, 7, 8]
posted @ 2026-01-12 13:04  yangykaifa  阅读(12)  评论(0)    收藏  举报