Set集合
概念
在 Java 中,Set 是集合框架中的一种接口,它表示不允许包含重复元素的集合。Set 接口继承自 Collection 接口,它没有提供额外的方法,但是它保证了集合中不会包含相同的元素。Set 接口的主要实现类有 HashSet、LinkedHashSet、TreeSet 等。
特点
- 无序:Set 不保证元素的顺序,即元素不按照特定的顺序存储和访问。
- 不可重复:不允许包含重复的元素。如果试图向 Set 中添加已经存在的元素,add 方法将返回 false。
- 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
HashSet(掌握)
概念
HashSet 是 Java 中的一个集合类,它实现了 Set 接口。Set 是一种不允许包含重复元素的集合,而 HashSet 则是 Set 接口的一个具体实现。因此,HashSet 用于存储一组唯一的元素,不允许重复。
特点
- 不允许重复元素:如果试图向 HashSet 中添加重复的元素,重复元素将被忽略。
- 无序性:HashSet 不保证元素的顺序,元素在 HashSet 中是无序的。
- 允许 null 元素:HashSet 可以包含一个 null 元素。
创建和初始化 HashSet
要使用 HashSet,首先需要创建一个 HashSet 对象。可以使用以下方式进行创建和初始化:
创建空的 HashSet
Set<String> set = new HashSet<>();
创建包含元素的 HashSet
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
基本操作
添加元素
要向 HashSet
中添加元素,可以使用 add()
方法:
Set<String> colors = new HashSet<>();
colors.add("红色");
colors.add("绿色");
colors.add("蓝色");
删除元素
要从 HashSet
中删除元素,可以使用 remove()
方法:
Set<String> fruits = new HashSet<>(Arrays.asList("苹果", "香蕉", "橙子"));
fruits.remove("香蕉");
判断元素是否存在
可以使用 contains()
方法来检查元素是否存在于 HashSet
中:
Set<String> animals = new HashSet<>(Arrays.asList("狗", "猫", "鸟"));
boolean containsCat = animals.contains("猫");
获取集合大小
要获取 HashSet
中元素的数量,可以使用 size()
方法:
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
int size = numbers.size();
遍历 HashSet
遍历 HashSet
中的元素可以使用迭代器或增强型 for 循环。以下是两种遍历方式的示例:
使用迭代器遍历
Set<String> colors = new HashSet<>(Arrays.asList("红色", "绿色", "蓝色"));
Iterator<String> iterator = colors.iterator();
while (iterator.hasNext()) {
String color = iterator.next();
System.out.println(color);
}
使用增强型 for 循环遍历
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
for (int number : numbers) {
System.out.println(number);
}
TreeSet(掌握)
概念
TreeSet 是 Java 集合框架中的一种有序集合,它实现了 Set 接口,因此具有不允许重复元素的特性。与 HashSet 不同,TreeSet 使用红黑树数据结构来存储元素,这使得元素在集合中保持有序。
特点
-
有序性(Order):TreeSet 中的元素按照自然排序(元素的自然顺序)或者指定的排序方式(通过比较器)排列。这意味着您可以遍历 TreeSet 得到的元素是按照一定的顺序排列的。
-
唯一性(Uniqueness):与 HashSet 一样,TreeSet 也保证元素的唯一性,不允许重复元素。
因此,TreeSet 是一个适用于需要有序存储唯一元素的场景的理想选择
TreeSet 的内部实现
要深入理解 TreeSet,我们需要了解它的内部实现机制,即红黑树。红黑树是一种自平衡二叉搜索树(Self-Balancing Binary Search Tree),它具有以下特性:
-
每个节点要么是红色,要么是黑色。
-
根节点是黑色。
-
每个叶子节点(NIL 节点,空节点)是黑色的。
-
如果一个节点是红色的,则它的两个子节点都是黑色的。
-
从任意节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。
这些规则确保了树的平衡,从而保证了树的高度不会过高,使得查找、插入和删除操作的性能稳定。
在 TreeSet 中,元素被存储在红黑树的节点中,根据元素的大小关系构建树结构。这意味着,插入、删除和查找操作的时间复杂度为 O(log n),其中 n 是集合中的元素个数。由于红黑树的平衡性质,这些操作的性能是可预测的。
TreeSet 的创建与初始化
默认构造函数
使用默认构造函数创建一个空的 TreeSet
对象:
TreeSet<String> treeSet = new TreeSet<>();
指定排序方式的构造函数
您可以使用带有 Comparator
参数的构造函数来指定元素的排序方式。比如,创建一个降序排列的 TreeSet
:
TreeSet<Integer> customOrderTreeSet = new TreeSet<>(Comparator.reverseOrder());
从现有集合创建
您还可以从现有的集合(如 List
或 Set
)创建一个 TreeSet
,以便在不同集合类型之间进行转换:
Set<String> existingSet = new HashSet<>(Arrays.asList("A", "B", "C"));
TreeSet<String> treeSetFromSet = new TreeSet<>(existingSet);
基本操作
添加元素
使用 add
方法来向 TreeSet
中添加元素:
treeSet.add("D");
treeSet.add("E");
删除元素
使用 remove
方法来从 TreeSet
中删除元素:
treeSet.remove("B");
查询元素是否存在
使用 contains
方法来检查元素是否存在于 TreeSet
中:
boolean containsC = treeSet.contains("C");
遍历 TreeSet
使用迭代器遍历
Iterator<String> iterator = treeSet.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}
使用增强的 for 循环遍历
for (String element : treeSet) {
System.out.println(element);
}
LinkedHashSet(掌握)
概念
LinkedHashSet 是 Java 集合框架中的一种类,它继承自 HashSet,因此具有哈希表的查找性能,同时又使用链表维护元素的插入顺序
特点
- 有序性(Order):
LinkedHashSet
会保持元素的插入顺序,即元素被添加到集合中的顺序就是它们在集合中的顺序。 - 唯一性(Uniqueness):与
HashSet
一样,LinkedHashSet
保证元素的唯一性,不允许重复元素。
因此,LinkedHashSet
是一个适用于需要按照插入顺序存储唯一元素的场景的理想选择。
LinkedHashSet 的创建与初始化
默认构造函数
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
指定容量和加载因子
int initialCapacity = 20;
float loadFactor = 0.5f;
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>(initialCapacity, loadFactor);
从现有集合创建
还可以从现有的集合(如 List
或 Set
)创建一个 LinkedHashSet
,以便在不同集合类型之间进行转换:
Set<String> existingSet = new HashSet<>(Arrays.asList("A", "B", "C"));
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>(existingSet);
基本操作
添加元素
使用 add
方法来向 LinkedHashSet
中添加元素:
linkedHashSet.add("D");
linkedHashSet.add("E");
删除元素
使用 remove
方法来从 LinkedHashSet
中删除元素:
linkedHashSet.remove("B");
查询元素是否存在
使用 contains
方法来检查元素是否存在于 LinkedHashSet
中:
boolean containsC = linkedHashSet.contains("C");
遍历 LinkedHashSet
使用迭代器遍历
Iterator<String> iterator = linkedHashSet.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}
使用增强的 for 循环遍历
for (String element : linkedHashSet) {
System.out.println(element);
}
注意事项
在使用 LinkedHashSet 时,有一些注意事项需要考虑:
- LinkedHashSet 是线程不安全的,如果在多线程环境中使用,需要考虑线程同步的问题,或者考虑使用线程安全的集合类如 ConcurrentLinkedHashSet。
- LinkedHashSet 允许存储一个 null 元素,但通常建议避免将 null 作为有效元素存储,以免混淆和错误。
- 当使用自定义对象作为 LinkedHashSet 元素时,需要正确实现 hashCode() 和 equals() 方法,以确保对象在集合中的唯一性和正确性。
- LinkedHashSet 的性能通常是很高的,但在处理大量数据时,应注意负载因子的设置,以避免频繁的扩容操作。
EnumSet(了解)
EnumSet是专门为枚举类涉及的集合类
-
EnumSet的所有元素都必须是指定枚举类型的枚举值
-
EnumSet的集合元素是有序的()由枚举类的定义顺序决定存储顺序
-
EnumSet在内部采用位向量形式存储–存储紧凑、高效,占用内存小、运行效率高,适合批量操作
-
EnumSet不允许添加重复元素,如果添加重复值,则会将上一次的值覆盖
-
不允许添加null,如果添加null,则会报空指针异常NullPointerException
CopyOnWriteArraySet(了解)
-
CopyOnWriteArraySet继承于AbstractSet,这就意味着它是一个集合
-
CopyOnWriteArraySet包含CopyOnWriteArrayList对象,它是通过CopyOnWriteArrayList实现的,而CopyOnWriteArrayList本质是个动态数组队列
-
所以CopyOnWriteArraySet相当于通过通过动态数组实现的“集合”!
-
CopyOnWriteArrayList中允许有重复的元素;但是,CopyOnWriteArraySet是一个集合,所以它不能有重复集合
-
因此,CopyOnWriteArrayList额外提供了addIfAbsent()和addAllAbsent()这两个添加元素的API,通过这些API来添加元素时,只有当元素不存在时才执行添加操作!
-
至于CopyOnWriteArraySet的“线程安全”机制,和CopyOnWriteArrayList一样,是通过volatile和互斥锁来实现的。