`HashSet` 是 Java 中一个重要的集合类,它实现了 `Set` 接口,提供了对集合中元素的快速查找、添加和删除操作。`HashSet` 的底层原理主要依赖于哈希表(HashTable)来实现,它使用哈希函数来索引元素,并存储元素的实际位置。
以下是 `HashSet` 的主要底层原理:
1. 数组:`HashSet` 内部维护了一个数组,这个数组被称为“桶”(buckets)。每个桶用来存储一个或多个元素。当创建 `HashSet` 时,如果没有指定初始容量,它会使用一个默认的容量大小(通常是 16)。
2. 哈希函数:当向 `HashSet` 添加一个元素时,会使用哈希函数来计算元素的哈希码(hash code),然后使用这个哈希码来确定元素应该存储在数组的哪个桶中。
3. 链表:如果两个不同的元素产生了相同的哈希码或者不同的哈希码映射到了同一个桶,这时会发生“哈希冲突”。`HashSet` 通过在数组中使用链表来解决哈希冲突。每个桶中可以存储多个元素,它们通过链表连接起来。
4. 红黑树:在 Java 8 之后,为了提高性能,当链表中的元素数量超过一定阈值(默认为 8)时,链表会被转换为红黑树。红黑树是一种自平衡的二叉搜索树,它可以更高效地处理大量的哈希冲突。
5. 扩容:当 `HashSet` 中的元素数量达到容量和负载因子(load factor)的乘积时,`HashSet` 会进行扩容操作,即创建一个新的更大的数组,并将所有现有的元素重新哈希到新数组中。这个过程通常称为“rehashing”。
6. 迭代器:`HashSet` 提供了迭代器(Iterator),用于遍历集合中的所有元素。迭代器是 fail-fast 的,这意味着如果在迭代过程中集合结构被修改,迭代器会立即抛出 `ConcurrentModificationException`。
7. 容量和大小:`HashSet` 中有两个重要的概念:容量(capacity)和大小(size)。容量指的是数组的长度,而大小指的是集合中实际包含的元素的数量。
`HashSet` 的主要操作,如 `add(E)`、`contains(E)`、`remove(E)` 等,都是通过计算元素的哈希码来定位到相应的桶,然后在桶中的链表(或红黑树)中查找、插入或删除节点。
扩容操作会在数组的元素数量达到阈值时触发,此时会创建一个新的更大的数组,并将所有现有的元素重新哈希到新数组中。这个过程中,原有的链表可能会被转换为红黑树,以提高在大量元素情况下的性能。
`HashSet` 的迭代器也是 fail-fast 的,这意味着在迭代过程中如果集合结构被修改,迭代器会立即抛出 `ConcurrentModificationException`。
10-19
869
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)