一、TreeSet介绍
TreeMap是底层基于TreeMap的NavigableSet实现,容器的元素存储在TreeMap键值对映射的key中,它使用元素的自然顺序或者传入的比较器Comparator对元素进行排序。它为基本操作例如add、remove、contain提供了log(n)的时间复杂度保证。注意TreeSet不是线程同步的容器,如果有多个线程同时访问它,且至少有一个线程修改了容器的结构,那么他必须在外部进行同步处理。一般都是通过对自然封装该Set实例的对象进行同步操作来完成,如果没有这样的对象,那么可以使用Collections.synChronizedSortedSet包装TreeSet为一个线程安全的容器。
该TreeSet容器返回的迭代器是快速失败的(fail-fast),即在生成迭代器之后,如果该容器发生了结构部性修改除了通过迭代iterator.remove方法删除元素之外,迭代器将会抛出ConcurrentModificationException异常,该异常并不能绝对保证,应该只用于检测异常。
二、TreeSet数据结构
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
/** 内部TreeMap实例 **/
private transient NavigableMap<E,Object> m;
//静态对象充当TreeMap实例中的键值对中的value,所有key的value都是该对象
private static final Object PRESENT = new Object();
}
TreeSet继承自AbstractSet,AbstractSet提供了Set的一些基本方法实现,大大减少实现TreeSet的工作量,此外它实现了NavigableSet接口,扩展了倒序、获取给定目标最接近匹配等功能,实现了Clonable支持克隆、java.io.Serializable支持序列化
三、TreeSet的源码分析
1 - 构造函数
/**
* 构造方法,指定NavigableMap实例
*/
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
/**
* 无参构造方法,默认创建一个TreeMap实例
*/
public TreeSet() {
this(new TreeMap<E,Object>());
}
/**
* 构造方法,创建一个空的TreeMap实例并按照方法指定的比较器对元素进行排序
*/
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
/**
* 构造方法,创建一个空的TreeSet实例,并将方法指定集合c的所有元素填充到TreeSet实例中
*/
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* 构造方法,创建一个空的TreeSet实例,比较器取方法参数SortedSet对象的s的内部比较器,
* 将容器s中的所有元素填充到内部TreeMap实例
*/
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
通过对TreeSet构造函数的分析,我们可以了解到TreeSet的底层与HashSet类似,只不过它的底层存储结构是一个NavagableMap实例,一般是TreeMap对象,所有TreeSet的操作最终都是反映到底层对TreeMap的操作。
2 - 其他成员方法
/**
* 返回一个遍历当前容器元素的迭代器
*/
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
}
/**
* 判断当前容器是否为空
*/
public boolean isEmpty() {
return m.isEmpty();
}
/**
* 返回当前容器是否包含指定元素
*/
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;
}
/**
* 获取容器第一个元素
*/
public E first() {
return m.firstKey();
}
/**
* 获取容器最后一个元素
*/
public E last() {
return m.lastKey();
}
通过对TreeSet成员方法的分析,我们可以发现它们的实现完全依赖于底层的NavigableMap实例(一般情况下可以简单视为TreeMap实例),容器元素对应的是TreeMap实例键值对的Key,若想研读底层实现可以直接去研读TreeMap响应方法的源码。