0. 你需要知道的TreeMap的内置属性
0.1 节点属性
K key; // 键
V value; // 值
Entry<K,V> left; // 左子节点
Entry<K,V> right; // 右子节点
Entry<K,V> parent; // 父节点
boolean color; // 节点的颜色
0.2 成员变量
//比较器对象private final Comparator<? super K> comparator;//根节点private transient Entry<K,V> root;//集合的长度private transient int size = 0;
1. TreeMap的几种构造方法
1.1 空参构造
解释:当我们进行空参初始化TreeMap时,TreeMap底层只做了一件事,就是初始化比较器为空
1.2 带比较器参数的构造方法
解释:当我们进行初始化TreeMap时,传入了一个比较器对象,TreeMap底层也只做了一件事,就是将你传入的比较器对象赋值给当前的comparator对象
1.3 带SortedMap参数的构造函数
解释:当我们初始化TreeMap的时候,传入了一个SortedMap对象,TreeMap底层做了两件事,一是将SortedMap中的比较器对象赋值给当前TreeMap的比较器,然后根据SortedMap的规则进行转换为TreeMap
1.4 带Map参数的构造函数
解释:当初始化TreeMap时,传入了一个Map对象(非SortedMap对象),TreeMap底层做了两件事,一是置空比较器,二是将当前map中的数据存入TreeMap中
2. put方法
2.1 源码
private V put(K key, V value, boolean replaceOld) {Entry<K,V> t = root;if (t == null) {addEntryToEmptyMap(key, value);return null;}int cmp;Entry<K,V> parent;// split comparator and comparable pathsComparator<? 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 {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;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);}addEntry(key, value, parent, cmp < 0);return null;}
2.2 逐行分析
private V put(K key, V value, boolean replaceOld) {//获取根节点的地址值,赋值给局部变量tEntry<K,V> t = root;//判断根节点是否为null//如果为null,表示当前是第一次添加,会把当前要添加的元素,当做根节点//如果不为null,表示当前不是第一次添加,跳过这个判断继续执行下面的代码if (t == null) {//方法的底层,会创建一个Entry对象,把他当做根节点addEntryToEmptyMap(key, value);//表示此时没有覆盖任何的元素return null;}//表示两个元素的键比较之后的结果int cmp;//表示当前要添加节点的父节点Entry<K,V> parent;//表示当前的比较规则//如果我们是采取默认的自然排序,那么此时comparator记录的是null,cpr记录的也是null//如果我们是采取比较去排序方式,那么此时comparator记录的是就是比较器Comparator<? super K> cpr = comparator;//表示判断当前是否有比较器对象//如果传递了比较器对象,就执行if里面的代码,此时以比较器的规则为准//如果没有传递比较器对象,就执行else里面的代码,此时以自然排序的规则为准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 {V oldValue = t.value;if (replaceOld || oldValue == null) {t.value = value;}return oldValue;}} while (t != null);} else {//把键进行强转,强转成Comparable类型的//要求:键必须要实现Comparable接口,如果没有实现这个接口//此时在强转的时候,就会报错。Comparable<? super K> k = (Comparable<? super K>) key;do {//把根节点当做当前节点的父节点parent = t;//调用compareTo方法,比较根节点和当前要添加节点的大小关系cmp = k.compareTo(t.key);if (cmp < 0)//如果比较的结果为负数//那么继续到根节点的左边去找t = t.left;else if (cmp > 0)//如果比较的结果为正数//那么继续到根节点的右边去找t = t.right;else {//如果比较的结果为0,会覆盖V oldValue = t.value;if (replaceOld || oldValue == null) {t.value = value;}return oldValue;}} while (t != null);}//就会把当前节点按照指定的规则进行添加addEntry(key, value, parent, cmp < 0);return null;}
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;elseparent.right = e;//添加完毕之后,需要按照红黑树的规则进行调整fixAfterInsertion(e);size++;modCount++;}
private void fixAfterInsertion(Entry<K,V> x) {//因为红黑树的节点默认就是红色的x.color = RED;//按照红黑规则进行调整//parentOf:获取x的父节点//parentOf(parentOf(x)):获取x的爷爷节点//leftOf:获取左子节点while (x != null && x != root && x.parent.color == RED) {//判断当前节点的父节点是爷爷节点的左子节点还是右子节点//目的:为了获取当前节点的叔叔节点if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//表示当前节点的父节点是爷爷节点的左子节点//那么下面就可以用rightOf获取到当前节点的叔叔节点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 {//表示当前节点的父节点是爷爷节点的右子节点//那么下面就可以用leftOf获取到当前节点的叔叔节点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;}
说明:本文大部分代码说明来自黑马程序员b站阿伟视频,在这里力推b站黑马程序员Java从入门到起飞课程,b站直达:https://www.bilibili.com/video/BV17F411T7Ao/?spm_id_from=333.337.search-card.all.click