1

Set实现

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

通用Set实现

有三种通用的Set实现 — HashSetTreeSetLinkedHashSet,使用这三种中的其中一种通常比较简单。HashSetTreeSet快得多(大多数操作的恒定时间与对数时间),但不提供排序保证,如果需要使用SortedSet接口中的操作,或者需要按值排序的迭代,请使用TreeSet,否则,请使用HashSet。可以肯定的是,大多数情况下你最终都会使用HashSet

LinkedHashSet在某种意义上介于HashSetTreeSet之间,作为哈希表实现,并在其中运行链表,它提供了按插入顺序排序的迭代(最近插入到最新),并且运行速度几乎与HashSet一样快。LinkedHashSet实现将其客户端从HashSet提供的未指定的、通常混乱的顺序中解放出来,而不会增加与TreeSet相关的成本。

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

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

HashSet类还有另一个调整参数,称为负载因数,如果你非常在意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方法所需的时间与set的大小成比例,此实现仅适用于很少修改但经常迭代的集合,它非常适合维护必须防止重复的事件处理列表。


上一篇:集合实现

博弈
2.5k 声望1.5k 粉丝

态度决定一切