Set接口概述

Set是Java集合框架中不允许重复元素的集合,不保证元素顺序

核心特征

Set接口继承自Collection接口,具有以下核心特性:

不允许重复元素

Set中不能包含重复的元素,每个元素都是唯一的

不保证顺序

Set不保证维护元素的插入顺序(LinkedHashSet除外)

元素判等机制

基于equals()和hashCode()方法判断元素是否重复

重要提示

存储在Set中的对象必须正确实现equals()和hashCode()方法,否则可能导致不可预期的行为。

HashSet详解

基于哈希表的Set实现,提供最佳查找性能

核心特性

  • 基于HashMap实现,使用哈希表存储数据
  • 不保证元素的顺序(无序集合)
  • 允许null元素
  • 添加、删除和查找操作的时间复杂度为O(1)
1

创建HashSet

import java.util.HashSet; import java.util.Set; // 创建HashSet实例 Set<String> hashSet = new HashSet<>(); // 创建带有初始容量的HashSet Set<Integer> numbers = new HashSet<>(20); // 从现有集合创建 Set<Double> prices = new HashSet<>(existingCollection);
2

基本操作

// 添加元素 hashSet.add("Apple"); hashSet.add("Banana"); hashSet.add("Orange"); // 尝试添加重复元素 boolean added = hashSet.add("Apple"); // 返回false // 删除元素 hashSet.remove("Banana"); // 检查元素是否存在 boolean hasApple = hashSet.contains("Apple"); // true // 获取大小 int size = hashSet.size(); // 2

性能优化

HashSet的默认初始容量为16,负载因子为0.75。当元素数量达到容量*负载因子时,集合会自动扩容。在已知元素数量时设置合适的初始容量可以避免不必要的扩容操作,提升性能。

TreeSet详解

基于红黑树的有序Set实现,元素自动排序

核心特性

  • 基于TreeMap实现,使用红黑树存储数据
  • 元素按自然顺序或自定义Comparator排序
  • 不允许null元素
  • 添加、删除和查找操作的时间复杂度为O(log n)
1

创建TreeSet

import java.util.TreeSet; import java.util.Set; import java.util.Comparator; // 自然顺序排序 Set<String> treeSet = new TreeSet<>(); // 自定义排序(按字符串长度) Set<String> lengthSortedSet = new TreeSet<>( Comparator.comparingInt(String::length) ); // 从现有集合创建(元素必须实现Comparable) Set<Integer> numbers = new TreeSet<>(existingCollection);
2

排序操作

// 添加元素 treeSet.add("Banana"); treeSet.add("Apple"); treeSet.add("Orange"); // 元素自动排序 System.out.println(treeSet); // [Apple, Banana, Orange] // 获取第一个和最后一个元素 String first = treeSet.first(); // "Apple" String last = treeSet.last(); // "Orange" // 范围查找(子集) Set<String> subSet = treeSet.subSet("Apple", "Orange");

重要限制

存储在TreeSet中的元素必须实现Comparable接口,或者在创建TreeSet时提供Comparator。否则在添加元素时会抛出ClassCastException。

LinkedHashSet详解

维护插入顺序的HashSet实现

核心特性

  • 继承自HashSet,使用链表维护插入顺序
  • 元素按插入顺序排序
  • 允许null元素
  • 性能略低于HashSet,但提供了可预测的迭代顺序
1

创建与基本操作

import java.util.LinkedHashSet; import java.util.Set; // 创建LinkedHashSet Set<String> linkedHashSet = new LinkedHashSet<>(); // 添加元素 linkedHashSet.add("Apple"); linkedHashSet.add("Banana"); linkedHashSet.add("Orange"); // 迭代顺序与插入顺序一致 for (String fruit : linkedHashSet) { System.out.println(fruit); // Apple, Banana, Orange } // 删除第一个元素 linkedHashSet.remove("Apple"); // 添加新元素到末尾 linkedHashSet.add("Mango");
2

访问顺序特性

// 插入顺序维护 Set<Integer> numbers = new LinkedHashSet<>(); numbers.add(3); numbers.add(1); numbers.add(4); numbers.add(2); System.out.println(numbers); // [3, 1, 4, 2] // 即使元素被访问,顺序也不会改变 numbers.contains(1); // 访问元素 System.out.println(numbers); // 仍为 [3, 1, 4, 2]

使用场景

LinkedHashSet非常适合需要维护插入顺序,同时又要快速查找元素的场景。例如实现LRU缓存时,可以结合LinkedHashMap使用。

HashSet、TreeSet与LinkedHashSet对比

全面比较三种Set实现的特性与适用场景

HashSet

无序集合

性能最佳

允许null

TreeSet

排序集合

自动排序

禁止null

LinkedHashSet

插入顺序

快速迭代

允许null

特性 HashSet TreeSet LinkedHashSet
底层实现 哈希表 红黑树 哈希表 + 双向链表
排序保证 自然顺序或自定义顺序 插入顺序
允许null元素
add/contains性能 O(1) O(log n) O(1)
迭代性能 慢(无顺序) 中(有序) 快(链表顺序)
内存占用 高(维护链表)
最佳使用场景 需要快速查找,不关心顺序 需要元素排序 需要维护插入顺序
O(1)
HashSet
O(log n)
TreeSet
O(1)
LinkedHashSet

常见错误与最佳实践

避免Set使用中的陷阱,掌握高效使用技巧

常见错误

  • 未正确实现equals/hashCode导致重复元素
  • 在TreeSet中使用未实现Comparable的对象
  • 在迭代过程中修改集合导致ConcurrentModificationException
  • 误认为HashSet是有序集合
  • 对大集合使用contains()性能差

最佳实践

  • 存储自定义对象时始终重写equals/hashCode
  • 在已知元素数量时设置HashSet的初始容量
  • 使用TreeSet时提供Comparator而不是依赖自然顺序
  • 只读遍历使用Iterator而不是forEach+remove
  • 对集合使用containsAll()代替嵌套循环

动手练习

  • 实现一个自定义类,存储在HashSet中并正确重写equals/hashCode
  • 创建TreeSet,按学生成绩降序排列
  • 使用LinkedHashSet实现一个最近访问项列表
  • 比较不同Set实现的10万次添加操作的性能
  • 实现一个Set去重工具类