集合框架之Set接口

时间:2022-04-15 17:02:51


一个不包含重复元素的 collection。更确切地讲,set 不包含满足e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素

在所有构造方法以及 add、equals 和 hashCode 方法的协定上,Set 接口还加入了其他规定,这些规定超出了从 Collection 接口所继承的内容。

Set不能包含重复的元素,它的所有方法都是从Collection接口继承的,并且对一些方法加上了相应的限制。Set还增强了equals方法和hasCode方法的可用性,允许对实现类型不一样的实例进行有意义的对比

Set接口定义的方法与Collection一致,在此不再列出,仅对Set的批量操作稍加说明。假设s1和s2都是Set类型,下面给出一些例子

s1.containsAll(s2) — 如果s2是s1的子集,返回true;反之,返回false

s1.addAll(s2) — 将s1和s2的并集存入s1

s1.retainAll(s2) — 将s1和s2的交集存入s1

s1.removeAll(s2) — 移除s1中与s2重合的元素

 

Java平台提供了三种通用实现类:HashSet,TreeSet和LinkedHashSet。

HashSet用哈希表作为容器,是性能最好的实现类,但是它不保证迭代顺序,元素的顺序不是固定的

TreeSet用红黑树作为容器,根据元素值进行排序,它的效率要比HashSet低很多

LinkedHashSet也是用哈希表实现的,不同的是,哈希表中运行着一个链表,元素按照插入的先后顺序排序。LinkedHashSet的元素顺序比混乱的HashSet要清晰很多,但是效率要比HashSet低

HashSet

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size,假定哈希函数将这些元素正确地分布在桶中。对此 set 进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层 HashMap 实例(桶的数量)的“容量”的和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

HashSet除了集合的通用实现类都有的无参构造函数和接受一个Collection作为参数的构造函数,还有两个特殊的构造函数:

HashSet(int initialCapacity)

          构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。

HashSet(int initialCapacity, float loadFactor)

          构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。

初始容量的默认值为16,加载因子的默认值为0.75

需要注意的是,HashSet不支持Collection接口中定义的批量操作方法,如addAll()、containsAll()、removeAll()、retainAll()

TreeSet

基于 TreeMap 的 NavigableSet接口的实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

注意,如果要正确实现 Set 接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。这是因为 Set 接口是按照 equals 操作定义的,但 TreeSet 实例使用它的 compareTo(或 compare)方法对所有元素进行比较,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,其行为也是定义良好的;它只是违背了 Set 接口的常规协定。

该实现类也有两个特殊的构造方法:

TreeSet(Comparator<?super E> comparator)

          构造一个新的空 TreeSet,它根据指定比较器进行排序。

TreeSet(SortedSet<E> s)

          构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet。

TreeSet扩展方法(即没有在Set接口中定义的方法):

E ceiling(E e)

          返回此 set 中大于等于给定元素的最小元素;如果不存在这样的元素,则返回 null。

Comparator<?super E> comparator()

          返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回 null。

 NavigableSet<E> descendingSet()

          返回此 set 中所包含元素的逆序视图。

 E first()

          返回此 set 中当前第一个(最低)元素。

 E floor(E e)

          返回此 set 中小于等于给定元素的最大元素;如果不存在这样的元素,则返回 null。

 SortedSet<E> headSet(E toElement)

          返回此 set 的部分视图,其元素严格小于toElement。

 NavigableSet<E> headSet(E toElement,boolean inclusive)

          返回此 set 的部分视图,其元素小于(或等于,如果 inclusive 为 true)toElement。

 E higher(E e)

          返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回 null。

 E last()

          返回此 set 中当前最后一个(最高)元素。

 E lower(E e)

          返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回 null。

 E pollFirst()

          获取并移除第一个(最低)元素;如果此set 为空,则返回 null。

 E pollLast()

          获取并移除最后一个(最高)元素;如果此set 为空,则返回 null。

 NavigableSet<E> subSet(E fromElement,boolean fromInclusive, E toElement, boolean toInclusive)

          返回此 set 的部分视图,其元素范围从fromElement 到 toElement。

 SortedSet<E> subSet(E fromElement, EtoElement)

          返回此 set 的部分视图,其元素从fromElement(包括)到 toElement(不包括)。

 SortedSet<E> tailSet(E fromElement)

          返回此 set 的部分视图,其元素大于等于fromElement。

 NavigableSet<E> tailSet(E fromElement,boolean inclusive)

         返回此 set 的部分视图,其元素大于(或等于,如果 inclusive 为 true)fromElement。

需要注意的是TreeSet实现了SortedSet接口,实现了该接口的子类中,视图操作(即subSet、tailSet等方法)与List有很大不同。List的视图操作在源列表修改后将会抛出异常,比如,我们截取一个使用subList方法截取List的一段元素作为操作对象,一旦源List进行了插入、删除等操作,则用subList获取的子列表将会抛出异常。而SortedSet的子类不会出现这种情况,因为使用subSet截取SortedSet的起始点是不是固定的某两个元素,而是其下标的绝对位置,对源集合的修改会同步到子集合当中,反之亦然。另外,Set的视图操作与List一样,是半开放的,即包含低位段,不包含高位段,比如,有一个Set包含的元素为字母a-g,subSet(‘a’,’d’)得到的结果是包含’a’、’b’、’c’的集合

LinkedHashSet

具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序不受在set 中重新插入的元素的影响。(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到 set s 中。)

此实现可以让客户免遭未指定的、由HashSet 提供的通常杂乱无章的排序工作,而又不致引起与 TreeSet 关联的成本增加。使用它可以生成一个与原来顺序相同的 set 副本,并且与原 set 的实现无关:

     void foo(Set s) {         Set copy = new LinkedHashSet(s);
...
}

 如果模块通过输入得到一个 set,复制这个 set,然后返回由此副本决定了顺序的结果,这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)

扩展的构造方法:

LinkedHashSet(int initialCapacity)

          构造一个带指定初始容量和默认加载因子(0.75) 的新空链接哈希 set。

LinkedHashSet(int initialCapacity, float loadFactor)

          构造一个带有指定初始容量和加载因子的新空链接哈希 set。

 

该实现类没有扩展的方法,与Set接口中定义的方法一致