1.源码简述
- TreeMap的排序可以基于对象自身实现的Comparable接口,也可以基于传入的比较器,两者都没有,那么会报错(
cannot be cast to java.lang.Comparable
)。 - modCount 是用于fail fast。主要用于检测并发修改异常问题
- 基于自定义比较器比较,key是否为null,可以自己决定,但是如果使用的是对象实现的Comparable接口,那么key不能为null。
- 删除分为三种情况,当被删除元素有两个子节点时,是通过后继节点进行删除的。(红黑树的红黑标记和旋转没有研究)
2.重要成员变量
// 自定义的排序比较器 创建TreeMap时传入
private final Comparator<? super K> comparator;
// 并发修改判断值
private transient int modCount = 0;
// 树形map的根节点
private transient Entry<K,V> root;
3. put(key,value) 新增元素
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
// 这段代码用于检测comparator是否存在,如果不存在那么对象需要实现Comparable接口
// 否则没有比较的意义
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
// treeMap中存在时,则直接进行值覆盖
return t.setValue(value);
} while (t != null);
}
else {
// 基于Comparable接口的对象 key 不能为null值
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
// treeMap中存在时,则直接进行值覆盖
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;
}
流程:
- 针对空根节点进行操作
- TreeMap中找相同key,存在则覆盖并返回
- TreeMap中找要插入元素的父节点
- 在父节点中的左右子节点进行插入
- 红黑树规则平衡
3.get(Object key) 查找元素
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
// 基于comparator比较器查找
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
// 基于Comparable接口的compareTo方法进行大小比较,查找元素
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;
}
查找的过程比较简单,就是便利二叉树。是不过比较条件一个是传入的comparator比较器,一个是自身的Comparable接口实现。
4.remove(Object key) 删除接口
public V remove(Object key) {
// 查找到对应元素
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
V oldValue = p.value;
// 删除树元素
deleteEntry(p);
// 返回删除元素
return oldValue;
}
删除元素:
规则:
被删除元素P的左子节点都小于被删除节点
被删除元素P的左子节点都大于被删除节点的父节点
被删除元素的右子节点都大于被删除元素节点的父节点
被删除元素的右子节点都大于被删除元素节点的父节点
情景
1.当前节点没有子节点,直接删除
2.当前节点有一个子节点,直接删除当前节点,并将子节点顶上就行了
3.当前节点有两个节点:
找前继节点(小于被删除元素的最大值,也就是被删除元素的左子节点的最右节点),将该节点替换到被删除元素,然后原被删除节点的左子节点被附加到前继节点的最左子节点上。
或者找后继节点(大于被删除元素的最小值,也就是被删除元素的右子节点的最左节点),将该节点替换到被删除元素,然后原被删除节点的右子节点被附加到后继节点的最右子节点上
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
// 下面这块是进行元素删除的过程,总共有三种情景
// 1. 当前元素无子节点
// 2. 当前元素只有一个子节点
// 3.当前元素有两个子节点,则需要通过使用后继节点的方式进行元素删除
// 在下面5,6,7节中分开分析
}
//在删除节点中会调用这个方法,主要是用来找后继节点的
static <K,V> TreeMap.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;
}
}
5.被删除节点为叶子节点时
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
// 此时replacement为null
if (replacement != null) {
// 这段不会走
} else if (p.parent == null) { // return if we are the only node.
root = null; // 为根元素时,直接将根元素置为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; // 解除p与p.parent的连接
}
}
6.被删除节点的子节点为单节点时
// 当前节点只有一个子节点时
// 只需要将当前节点的子节点与当前节点的父节点进行连接即可
// 获取子节点
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)
// 如果被删除元素就是根节点,则replacement晋升为根节点
root = replacement;
else if (p == p.parent.left)
// 如果被删除元素为其父节点的左子节点,则父节点的左子节点连接到被删除元素的子节点上
p.parent.left = replacement;
else
// 如果被删除元素为其父节点的左子节点,则父节点的左子节点连接到被删除元素的子节点上
p.parent.right = replacement;
// 解除被删除元素的连接关系
p.left = p.right = p.parent = null;
// Fix replacement
if (p.color == BLACK)
// 红黑树的红黑控制与左右旋转
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node.
// 不会走到
} else { // No children. Use self as phantom replacement and unlink.
// 不会走到
}
7.被删除元素有两个子节点(情景1:后继节点没有右子节点)
后继节点是不可能有左子节点的
// 在successor方法中获取后继节点
// 将后继节点的键与值复制给被删除节点
// 将p指向后继节点(此时被删除的元素变为后继节点了,因为后继节点的值已经给)
// 这里的p已经不是被删除元素,而是后继节点
// 此时,后继节点没有子元素
Entry<K,V> replacement = null;
if (replacement != null) {
//这里不会走到
} else if (p.parent == null) { // return if we are the only node.
root = null; // 后继节点的父节点不可能为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; // 解除后继节点与父节点之间的连接
}
}
8.被删除元素有两个子节点(情景2:后继节点有右子节点)
在后继节点有右子节点的情况下
// 后继节点有右子节点,所以这里replacement不为null
Entry<K,V> replacement = p.right;
if (replacement != null) {
// 将后继节点的子节点与后继节点的父节点关联
replacement.parent = p.parent;
if (p.parent == null)
// 这个走不到
root = replacement;
else if (p == p.parent.left)
// p一定是右子节点,走不到
p.parent.left = replacement;
else
// 后继节点的父节点的右子节点指向后继节点的子节点
p.parent.right = replacement;
// 解除后继节点与子节点和父节点之间的关联
p.left = p.right = p.parent = null;
// Fix replacement
if (p.color == BLACK)
// 红黑关系调整和左右旋转
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node.
// 不走
} else { // No children. Use self as phantom replacement and unlink.
// 不走
}