Set Implementations

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

通用集实现

有三种通用的Set实现— HashSetTreeSetLinkedHashSet。通常使用这三个中的哪一个很简单。 HashSetTreeSet快得多(大多数操作的恒定时间与日志时间比较),但没有提供任何 Order 保证。如果需要使用SortedSetinterface中的操作,或者需要按值排序的迭代,请使用TreeSet;否则,使用HashSet。可以肯定的是,大多数情况下您final会使用HashSet

LinkedHashSet在某种意义上介于HashSetTreeSet之间。实现为散列表,散列表一直在其中,它提供了“插入 Sequences”迭代(最近插入到最新),并且运行速度几乎与HashSet一样。 LinkedHashSet实现将其 Client 端从HashSet提供的未指定的,通常是混乱的 Sequences 中省去,而不会导致与TreeSet相关的增加的成本。

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

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

HashSet类还有另一个调整参数,称为* load factor *。如果您非常关心HashSet的空间消耗,请阅读HashSet文档以获取更多信息。否则,只需接受默认值即可;这几乎总是正确的做法。

如果您接受默认的负载系数,但要指定初始容量,则选择一个数字,该数字大约是您希望集合增 Long 的大小的两倍。如果您的猜测还遥遥无期,那么您可能会浪费一些空间,时间或两者兼而有之,但这不太可能成为一个大问题。

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

专用集实现

有两个专用的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方法需要的时间与集合的大小成比例。此实现“仅*”适用于很少修改但经常迭代的集合。它非常适合维护必须防止重复的事件处理程序列表。