当这个问题第一时间问到我的时候,我以为TreeSet跟HashSet一样,底层都是通过hashMap来实现,后面发现其实不是,今天我们就来走一走TreeSet的底层源码。
运行版本:JDK1.8
首先看一下TreeSet的无参构造器,可以看到是TreeMap,没用到HashMap。
public TreeSet() {
this(new TreeMap<E,Object>());
}
这段代码又调用了一个新的构造器,我们往里面走了看一下。
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
将新创建出来的TreeMap对象赋值给了对象属性m,NavigableMap这是一个接口,具体的目前还没有了解过,我们先继续往下面走。
我们要想看到他怎么去重的,直接就去看他的add方法:
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
直接调用的是TreeMap的put方法,我们直接去看TreeMap源码
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
//对key做处理,检查是否为null
compare(key, key); // type (and possibly null) check
//给root赋值
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
//key对比结果
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
//比较器,如果我们在创建TreeMap的时候可以自定义一个比较器传入到里面
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
//如果传进来的key为空,直接抛出异常
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
//下面这个while循环是用来给key找到他在树中的位置
//如果有key相等,则值直接覆盖掉。
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
//如果在树种不存在key则,将新key插入树中
if (cmp < 0)
parent.left = e;
else
parent.right = e;
//红黑树处理
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
根据源码分析可以得到,TreeMap底层的数据结构是一个红黑树,如果新增一个key进来,首先会判断这key的值在树中是否存在,若存在则替换掉,若不存在则插入其中。