Set实现
Set实现分为通用实现和专用实现。
通用Set实现
有三种通用的Set实现 — HashSet、TreeSet和LinkedHashSet,使用这三种中的其中一种通常比较简单。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实现
有两个特殊用途的Set实现 — EnumSet和CopyOnWriteArraySet。
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
方法所需的时间与set
的大小成比例,此实现仅适用于很少修改但经常迭代的集合,它非常适合维护必须防止重复的事件处理列表。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。