一、什么是TreeMap
TreeMap特点:添加的键是可排序、不重复、无索引的
TreeMap主要红黑树的数据结构实现
二、TreeMap源码
put方法
public V put(K key, V value) {
return put(key, value, true);
}
此处我们能够看到调用put方法后又调用了三个参数的put方法,而第三个参数作用就是是否替换掉重复键的值。
private V put(K key, V value, boolean replaceOld) {
//创建一个节点变量为root
Entry<K,V> t = root;
//如果节点变量是空,则调用addEntryToEmptyMap方法
if (t == null) {
addEntryToEmptyMap(key, value);
return null;
}
//下面的操作只有在根节点有值时才会运行
//初始化一个比较变量,用于比较键的大小
int cmp;
//初始化一个父亲节点
Entry<K,V> parent;
// split comparator and comparable paths
//赋值比较器,如果构造函数为空参则比较器为null
Comparator<? super K> cpr = comparator;
//如果在初始化时传参了比较器则if为true
if (cpr != null) {
do {
//parent记录下添加节点应该放在哪个父亲节点下
parent = t;
//此处使用传入的比较器,得出添加的节点和父系节点的key的结果值
cmp = cpr.compare(key, t.key);
//如果结果值小于0则t=t的左节点如果大于0则放在右节点。
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
//如果等于0则说明key相同,要将值(value)覆盖掉
else {
V oldValue = t.value;
if (replaceOld || oldValue == null) {
t.value = value;
}
return oldValue;
}
} while (t != null);
} else {
//此处处理使用的比较器不同其他操作与上面类似
Objects.requireNonNull(key);
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
//这里使用key类型实现的比较器
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else {
V oldValue = t.value;
if (replaceOld || oldValue == null) {
t.value = value;
}
return oldValue;
}
} while (t != null);
}
//添加节点,通过parent和cmp确定需要添加的位置
addEntry(key, value, parent, cmp < 0);
return null;
}
1、首次添加
三个参数的put方法上来先是创建了一个变量赋值根节点,判断根节点是否为空如果为空调用addEntryToEmptyMap方法。
private void addEntryToEmptyMap(K key, V value) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
}
这里先是调用了compare方法,将两个相同的key进行比较,本身没有什么意义。
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
此方法对key进行了类型强转,如果key没有继承Comparable则会报错,所以这里这个方法真实作用是检查key是否继承了Comparable。
例如:
public static void main(String[] args) {
Map<Object, Object> map = new TreeMap<>();
Student student = new Student();
map.put(student, 1);
}
我这里Student类并没有继承Comparable。
运行后则会报出
其报错位置就在compare方法内。
2、后续添加
中间获取parent和cmp的步骤请看注解
最后根据parent和cmp调用addEntry方法添加节点。
private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {
Entry<K,V> e = new Entry<>(key, value, parent);
if (addToLeft)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
}
该方法先创建了一个节点对象。确定父亲节点。再根据addToLeft确定放在父亲节点左边还是右边。最后调用fixAfterInsertion方法。
private void fixAfterInsertion(Entry<K,V> x) {
//添加的节点颜色默认为红色
x.color = RED;
//根据红黑树的红黑规则进行添加
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK;
}