TreeMap源码
1. 概述
1.1 描述
-
底层是红黑树,实现了NavigableMap接口,可以根据Key自然排序,也可以在构造方法上传递Comparator实现Map的排序
-
其中,containsKey、get、put、remove这些方法的时间复杂度是log(n)
-
有序的Map中,一般是使用compareTo或者Compare方法来比较key,只要该两个方法认为相等,在Map的角度就认为这两个元素相等
-
TreeMap非同步
1.2 继承关系
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/44ebe45f94796ff9358309ab1ce1eb76.png)
1.3 特点
- TreeMap实现了NavigableMap接口,而NavigableMap接口继承着SortedMap接口,致使TreeMap是有序的
- TreeMap的底层是红黑树,时间复杂度为log(n)
- 非同步
- 使用Comparator或者COmparable来比较key是否相等与排序的问题
2. 源码
2.1 属性
//该比较器用来对TreeMap排序,如果为空,则按照自然顺序排序
private final Comparator<? super K> comparator;
//红黑树根节点
private transient Entry<K,V> root;
//红黑树元素个数
private transient int size = 0;
//结构修改次数
private transient int modCount = 0;
2.2 构造方法
//默认使用自然排序
public TreeMap() {
comparator = null;
}
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
//指定了比较器
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}
2.3 put方法
public V put(K key, V value) {
//t为红黑树根节点
Entry<K,V> t = root;
//首次put时,红黑树还未初始化,
if (t == null) {
//类型可能为空检查
compare(key, key);
//创建新节点,作为根节点
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
//cmp:比较器返回值
int cmp;
//parent:要插入的父节点处
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
//找到key值对应的节点,直接覆盖value值
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
//key值匹配的节点,直接覆盖value
return t.setValue(value);
} while (t != null);
}
//构建树节点,按cmp选择插入左分支还是右分支
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;
}
2.4 get方法
//向外暴露的get方法
public V get(Object key) {
Entry<K,V> p = getEntry(key);
//找到节点返回value,找不到节点返回null
return (p==null ? null : p.value);
}
final Entry<K,V> getEntry(Object key) {
// 当比较器不为空时,使用自定义的比较器寻找对应节点并返回
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
// 当比较器为空时,使用默认的比较器
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
//p为红黑树根节点,使用默认比较器找到key值相等的节点并返回
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
2.5 remove方法
//删除指定节点
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--;
// If strictly internal, copy successor's element to p and then make p
// point to successor.
// 目标节点左右子树都不为空,
if (p.left != null && p.right != null) {
//寻找被删除节点的继承节点
Entry<K,V> s = successor(p);
//使用继承节点替换当前被删除节点
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children
// Start fixup at replacement node, if it exists.
// 开始从被替换节点开始修复二叉树结构
//当前被删除节点只有左子树或者右子树,拿到不为空的树进行修复
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
//左子树或右子树不为空
if (replacement != null) {
// Link replacement to parent
// 将替换节点连接到被删除节点的父节点上
replacement.parent = p.parent;
if (p.parent == null)
//如父节点为空,则设置为根节点
root = replacement;
//否则设置为父节点的左子节点或右子节点
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement;
// Null out links so they are OK to use by fixAfterDeletion.
// 空出无用的引用,以便删除修复函数使用
p.left = p.right = p.parent = null;
// 以被替换节点为值修复树结构
if (p.color == BLACK)
fixAfterDeletion(replacement);
//被删除的节点没有子节点也没有父节点,则该节点为根节点,将根节点置为空
} else if (p.parent == null) { // return if we are the only node.
root = null;
//被删除的节点没有子节点但是有父节点,直接将该节点删除
} else { // No children. Use self as phantom replacement and unlink.
//如果被删除节点是黑色节点,则需要进行树结构平衡
if (p.color == BLACK)
fixAfterDeletion(p);
//将与父节点的引用断开
if (p.parent != null) {
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
//寻找被删除节点的一下个节点
static <K,V> Entry<K,V> successor(Entry<K,V> t) {
if (t == null)
return null;
else if (t.right != null) {
Entry<K,V> p = t.right;
while (p.left != null)
p = p.left;
return p;
} else {
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
2.6 遍历方法
abstract class PrivateEntryIterator<T> implements Iterator<T> {
Entry<K,V> next;
Entry<K,V> lastReturned;
int expectedModCount;
PrivateEntryIterator(Entry<K,V> first) {
expectedModCount = modCount;
lastReturned = null;
next = first;
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
next = successor(e);
lastReturned = e;
return e;
}
final Entry<K,V> prevEntry() {
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
next = predecessor(e);
lastReturned = e;
return e;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// deleted entries are replaced by their successors
if (lastReturned.left != null && lastReturned.right != null)
next = lastReturned;
deleteEntry(lastReturned);
expectedModCount = modCount;
lastReturned = null;
}
}
successor(e)
方法:
- 返回目标节点的下一个节点,所谓下一个,就是按照次序排序之后的下一个。
- 从代码中可以看出,如果右子树不为空,就返回右子树中最小的结点。如果右子树为空,则在左子树中寻找最大的结点。
3. 总结
- TreeMap的底层是红黑树,能够实现该Map集合有序
- 如果在构造方法中传递了Comparator对象,那么就会以Comparator对象的方法进行比较。否则,则使用Comparable的compareTo方法来比较
- 如果使用的是compareTo方法来比较,key一定是不能为null,并且得实现了Comparable接口的。
- 即使是传入了Comparator对象,不用compareTo方法来比较,key也是不能为null的
- 源码中,Comparator和Comparable出现的频率是很高的,因为TreeMap实现有序要么就是外界传递进来Comparator对象,要么就使用默认key的Comparable接口 (实现自然排序)
要点:
- 由于底层是红黑树,那么时间复杂度可以保证为log(n)
- key不能为null,为null抛出NullPointException
- 想要自定义比较,在构造方法中传入Comparator对象,否则使用key的自然排序来进行比较
- TreeMap是非同步的,想要同步可以使用Collections来进行封装