特点
- 有序(可以自己设置排序规则)
- 底层是红黑树存储
- 不考虑扩容情况,有一个数据就添加一个结点
源码分析
构造器
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
public TreeSet() {
this(new TreeMap<E,Object>());
}
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
添加元素
put(K key, V value)代码整体分析:
- 首先if (t == null):就是判断这棵树有没有数据,没有数据就自己和自己比较,返回自己,装成一个Entry类型直接存入树根,return。
- 有数据的话,先获取比较器Comparator:Comparator<? super K> cpr = comparator;
- 有比较器的话(构造函数传入比较器)if (cpr != null):然后按照传入的比较器比较给出正数(往右孩子遍历)、负数(往左孩子遍历)、零(等于情况直接修改值)三种情况
- 没有比较器的话,就按照底层提供的比较器进行比较(有些数据必须要提供比较器,不提供的话报错)compareTo,还是得到三种情况
- 以上比较结束会有两种情况
- 第一种找到了”相同“元素,前面就直接return了:return t.setValue(value);
- 第二种,遍历到最后没找到相同元素,最后就到了null跳出循环。然后装入Entry中[Entry<K,V> e = new Entry<>(key, value, parent)],最后判断存放在左孩子还是右孩子,存入即可
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
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 {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) 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);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
思考
- 如果你存入一个比较器,是字符串长度的比较如下图
然后存入了三个值
test.add(“abc”);
test.add(“ab”);
test.add(“bcd”);
问:test里面保存了几个值?
答:两个值[abc, ab]
因为,我们传入的比较器,只比较了大小,所以当长度相同时compare返回0,也就是直接return了。