Set
这个接口模拟了数学集合抽象,是一个不包含重复元素的集合。更贴切地说,存在 e1、e2 两个元素,满足 e1.equals(e2) ,Set 会认为它们是同一个。并且 Set 最多保存一个 null 元素。不允许包含重复元素意味着将可变对象作为元素时,需要格外小心。
相比 List 接口,Set 少了一些索引方法,所以 Set 接口没有顺序的概念。
Set 接口的常见实现包括:
- HashSet
- TreeSet
- LinkedHashSet
- SortedSet (子接口)
- NavigableSet (SortedSet 的子接口)
HashSet
HashSet 直接实现了 Set 接口,底层实现实际上是一个 HashMap 。
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
因为底层实际上是 HashMap ,所以它是无序的,并且允许 null 元素。负载因子也是 0.75 。
它的操作方法:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
以保存的元素为 Key ,默认 value 是一个 Object 对象。
其他关于 HashMap 的原理参见 《散列表及其在JDK中的实现》
TreeSet
TreeSet 的继承结构比 HashSet 复杂:
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable
public interface NavigableSet<E> extends SortedSet<E>
public interface SortedSet<E> extends Set<E>
SortedSet
这个接口继承自 Set ,拓展了对元素排序的能力,可以通过自然存入顺序或特定的比较器进行排序。插入有序集合的所有元素都必须实现 Comparable 接口。
所有通用的排序集合实现类都应该有四个标准的构造函数:
- 一个无参构造函数,创建一个空集合,并按照元素自然顺序排序。
- 一个具有
Comparator类型的单个参数的构造函数,创建一个空集合,并根据指定的比较器进行排序。 - 一个具有
Collection类型的单个参数的构造函数,创建一个新的有序集合,该集合具有与其参数类型相同的元素,并根据自然顺序排序。 - 一个具有
SortedSet类型单个参数的构造函数,创建一个新的排序集合,该排序集合具有与参数集合相同的元素和顺序。
NavigableSet
使用一些导航方法拓展了 SortedSet ,返回给定目标的附近的元素。方法 lower、floor、ceiling、higher 分别返回小于、小于或等于、大于或等于、大于给定元素的元素。如果不存在返回 null。
可以按照升序或降序访问或遍历 NavigableSet 。此接口还定义了 pollFirst和 pollLast 方法,它们返回并删除最低的和最高的元素。
在允许 null 元素的集合中,上面的导航方法可能会返回不确定的值,然而在这种情况下,结果也可以通过 contains(null) 来消除歧义。为了避免此类问题,还是鼓励此接口的实现不允许插入 null 。(请注意,Comparable元素的排序集本质上不允许为空。)
TreeSet
基于 TreeMap 的 NavigableSet 实现。 元素使用它们的自然顺序或在集合创建时提供的 Comparator 进行排序,具体取决于使用的构造函数。 此实现为基本操作(添加、删除和包含)提供有保证的 log(n) 时间成本。
TreeSet 的底层实现是
private transient NavigableMap<E,Object> m; // TreeMap 是 NavigableMap 的实现
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
public TreeSet() {
this(new TreeMap<E,Object>());
}
它的所有操作方法,也都是真正通过调用 TreeMap 对应的操作方法来实现的:
public boolean contains(Object o) {
return m.containsKey(o);
}
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
TreeMap
这里只简单介绍一下它的特性。
基于红黑树的 NavigableMap 实现,可以根据 Key 进行自然排序,也可通过比较器指定排序条件。
此实现为 containsKey、get、put 和 remove 操作提供有保证的 log(n) 时间成本。 算法是对 Cormen、Leiserson 和 Rivest 的算法简介中的那些算法的改编。
该实现并不是线程安全的。
总结
- Set 一般要求元素不能重复
- 有序的 Set 可以通过继承
SortedSet接口来实现,Set 接口本身没定义排序能力。 - 底层实现基本都是 Map ,代理了 Map 的实际操作方法。