# 设置实现

> 原文： [https://docs.oracle.com/javase/tutorial/collections/implementations/set.html](https://docs.oracle.com/javase/tutorial/collections/implementations/set.html)

`Set`实现分为通用和专用实现。

## 通用集实现

有三种通用 [`Set`](https://docs.oracle.com/javase/8/docs/api/java/util/Set.html) 实现 - [`HashSet`](https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html) ， [`TreeSet`](https://docs.oracle.com/javase/8/docs/api/java/util/TreeSet.html) 和 [`LinkedHashSet`](https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashSet.html) 。使用这三种中的哪一种通常是直截了当的。 `HashSet`比`TreeSet`快得多（大多数操作的恒定时间与对数时间），但不提供订购保证。如果需要使用`SortedSet`接口中的操作，或者需要按值进行迭代，请使用`TreeSet`;否则，请使用`HashSet`。这是一个公平的赌注，你最终会在大部分时间内使用`HashSet`。

`LinkedHashSet`在某种意义上介于`HashSet`和`TreeSet`之间。实现为具有贯穿其的链表的哈希表，它提供 _ 插入顺序 _ 迭代（最近最少插入到最近）并且运行几乎与`HashSet`一样快。 `LinkedHashSet`实现使其客户免于`HashSet`提供的未指定的，通常是混乱的排序，而不会导致与`TreeSet`相关的增加的成本。

关于`HashSet`值得记住的一件事是，迭代在条目数和桶数（_ 容量 _）之和中是线性的。因此，选择太高的初始容量会浪费空间和时间。另一方面，选择一个太低的初始容量会在每次强制增加容量时复制数据结构，从而浪费时间。如果未指定初始容量，则默认值为 16.过去，选择素数作为初始容量有一些优势。这不再是真的。在内部，容量总是四舍五入到 2 的幂。使用`int`构造函数指定初始容量。以下代码行分配初始容量为 64 的`HashSet`。

```
Set<String> s = new HashSet<String>(64);

```

`HashSet`类还有一个称为 _ 加载因子 _ 的调整参数。如果您非常关心`HashSet`的空间消耗，请阅读`HashSet`文档以获取更多信息。否则，只接受默认值;这几乎总是正确的事情。

如果您接受默认加载因子但想要指定初始容量，请选择一个大约是您希望该组增长的大小的两倍的数字。如果您的猜测偏远，您可能会浪费一些空间，时间或两者，但这不太可能是一个大问题。

`LinkedHashSet`具有与`HashSet`相同的调整参数，但迭代时间不受容量影响。 `TreeSet`没有调整参数。

## 专用集实现

有两个专用`Set`实现 - [`EnumSet`](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html) 和 [`CopyOnWriteArraySet`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CopyOnWriteArraySet.html) 。

`EnumSet`是枚举类型的高性能`Set`实现。枚举集的所有成员必须具有相同的枚举类型。在内部，它由位向量表示，通常是单个`long`。枚举在枚举类型的范围上设置支持迭代。例如，给定星期几的枚举声明，您可以迭代工作日。 `EnumSet`类提供了一个简单的静态工厂。

```
    for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY))
        System.out.println(d);

```

枚举集还为传统的位标志提供了丰富的，类型安全的替代品。

```
    EnumSet.of(Style.BOLD, Style.ITALIC)

```

`CopyOnWriteArraySet`是由写时复制阵列备份的`Set`实现。所有的变异操作，例如`add`，`set`和`remove`，都是通过制作数组的新副本来实现的;不需要锁定。甚至迭代也可以安全地与元素插入和删除同时进行。与大多数`Set`实现不同，`add`，`remove`和`contains`方法需要与集合大小成比例的时间。这种实现仅适用于适用于很少修改但经常迭代的集合。它非常适合维护必须防止重复的事件处理程序列表。