文档

Java™ 教程-Java Tutorials 中文版
Set 实现
Trail: Collections
Lesson: Implementations

Set 实现

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

通用 Set 实现

有三种通用的 Set 实现 — HashSetTreeSetLinkedHashSet。使用这三种中的哪一种通常是直截了当的。HashSetTreeSet 快得多(大多数操作的常量时间与 log 时间),但不提供排序保证。如果需要使用 SortedSet 接口中的操作,或者需要按值进行迭代,请使用 TreeSet;否则,请使用 HashSet。可以肯定的是,大多数时候你最终都会使用 HashSet

LinkedHashSet 在某种意义上介于 HashSetTreeSet 之间。作为一个具有链表的哈希表来实现,它提供 insertion-ordered (插入顺序) 迭代(最早插入到最近插入)并且运行几乎与 HashSet 一样快。LinkedHashSet 实现使其客户端免于由 HashSet 提供的未指定的、通常是混乱的排序影响,而不会导致与 TreeSet 相关的增加的成本。

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

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

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

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

LinkedHashSetHashSet 具有相同的调整参数,但迭代时间不受容量影响。TreeSet 没有调整参数。

专用 Set 实现

有两个专用的 Set 实现 — EnumSetCopyOnWriteArraySet

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 实现。所有的可变操作,例如 addsetremove,都是通过制作数组的新副本来实现的;不需要锁定。甚至迭代也可以安全地与元素插入和删除同时进行。与大多数 Set 实现不同,addremovecontains 方法需要的时间与集合的大小成正比。此实现 适用于很少修改但经常迭代的集。它非常适合维护必须防止重复的事件处理程序列表。


Previous page: Implementations
Next page: List Implementations