前言
学java也快一年了,说实话TreeMap还真没怎么用过,也一直没去了解它。正因为这样,每次提到TreeMap都有点露怯,正好借着这次夯实基础,把TreeMap给认真摸一遍。
概述
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
复制代码
- 可以看到跟hashmap一样继承自AbstractMap,说明TreeMap也是键值对结构
- NavigableMap就是TreeMap的出彩之处,这个接口跟元素的排列大小顺序有关,具体怎么个有关法,我们后边介绍,这里知道就好啦。
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;
}
复制代码
这不是说曹操曹操到,刚说实现了一个跟顺序有关的接口,这里就出现了一个比较器。通过比较器实现TreeMap内部元素的排列顺序比较们。我们知道HashMap底层是个entry数组,这里则是树的根节点root,其实TreeMap底层就是红黑树,没错HashMap中当节点变多就会变成的那颗红黑树。我们可以看到每个树节点持有对左右子树以及父节点的引用。
接着来看一些常用的Api吧
初始化
public TreeMap() {
comparator = null;
}
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
复制代码
可以看到初始化的过程中都有比较器的身影,如果比较器是null,则会按照键的自然排序规则来排序,否则按照我们的自定义比较器来进行比较排序。
查找
public V get(Object key) {
Entry<K,V> p = getEntry(key);
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;
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;
}
复制代码
- 如果我们定义了比较器则使用自定义比较器比较规则来查找
- 可以看到key不能为空,否则会报NPE。
- 根据红黑树规则去比较树节点直到找到key相等的节点,否则返回null
添加
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;
}
复制代码
- 如果treeMap是空的,换言之root节点为空,compare(key,key)确保要添加的key不为null,然后由此key构造root节点
- 此时root不为空,如果比较器存在,根据比较器去找要添加的key对应的树节点,找到的话直接赋值value
- 如果比较器不存在,首先确保key不为空,使用默认比较规则比较找树节点,找到的话直接赋值value
- 如果上边两步没找到,构造新节点根据插入,再调整红黑树
总结
其实说到这里,也差不多了,这玩意儿不怎么常用,了解一下,如果真用到可能还是再去乖乖看一遍为好,毕竟早晚会忘不用的话。。只要知道TreeMap的核心是比较器,各种方法的核心基本都是基于比较器实现的,红黑树我真的没去好好看过,所以这里就基本跳了(逃,等后边有时间好好研究一下。另外,TreeMap的查找时间复杂度是O(log n),相比之下还是不如hashmap来的快,不过胜在有顺序,如果按自然顺序或者比较器规则来遍历元素,TreeMap更有优势。
公众号:程序员二狗
每日原创文章,一起交流学习