TreeMap 简介
TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。
TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。
TreeMap 实现了Cloneable接口,意味着它能被克隆。
TreeMap 实现了java.io.Serializable接口,意味着它支持序列化。
TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
TreeMap的基本操作 containsKey、get、put 和 remove 的时间复杂度是 log(n) 。
另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。
TreeMap的底层储存
TreeMap底层使用红黑树实现的,通过传入一个比较器来进行比较排序,比较器是实现Comparator接口的类:
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
或者通过键的自然顺序进行比较排序,自然顺序指的是实现Comparable接口:
public interface Comparable<T> {
public int compareTo(T o);
}
TreeMap底层使用Entry<K,V>键值对来构造红黑树,通过对KEY进行比较构造。
private final Comparator<? super K> comparator;//比较器
private transient Entry<K,V> root = null;//根节点
private transient int size = 0;//存储的数量
- TreeMap的本质是R-B Tree(红黑树),它包含几个重要的成员变量: root, size, comparator。
- root 是红黑数的根节点。它是Entry类型,Entry是红黑数的节点,它包含了红黑数的6个基本组成成分:key(键)、value(值)、left(左孩子)、right(右孩子)、parent(父节点)、color(颜色)。Entry节点根据key进行排序,Entry节点包含的内容为value。
- 红黑数排序时,根据Entry中的key进行排序;Entry中的key比较大小是根据比较器comparator来进行判断的。如果没有指定comparator,就通过键key的自然顺序排序,这就要求key实现了Comparable接口,否则插入时报错ClassCastException
- size是红黑数中节点的个数。
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left = null;//左孩子
Entry<K,V> right = null;//右孩子
Entry<K,V> parent;//父节点
boolean color = BLACK;//节点颜色,红还是黑
//方法省略。。。。。。
}
TreeMap构造函数
// 默认构造函数。使用该构造函数,TreeMap中的元素按照自然排序进行排列。
TreeMap()
// 创建的TreeMap包含Map
TreeMap(Map<? extends K, ? extends V> copyFrom)
// 指定Tree的比较器
TreeMap(Comparator<? super K> comparator)
// 创建的TreeSet包含copyFrom
TreeMap(SortedMap<K, ? extends V> copyFrom)
TreeMap的节点插入
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // 用来判断TreeMap是否有Comparator,或者key是否实现
//comparable接口,否则报ClassCastException
root = new Entry<>(key, value, null);//构造根节点
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
Comparator<? super K> cpr = comparator;
//如果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();
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;
}
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
可以看出TreeMap的插入是正常的二叉排序树的插入,最关键的是插入之后进行节点的调整,来保存整棵树的平衡。
fixAfterInsertion()函数就不多介绍了,具体看红黑树的插入调整,左旋和右旋,以及涂色。
TreeMap删除节点
public V remove(Object key) {
Entry<K,V> p = getEntry(key);//先找到这个key对应的键值对节点
if (p == null)
return null;
V oldValue = p.value;
deleteEntry(p);//删除这个键值对节点。
return oldValue;
}
//根据key查找对应的键值对节点
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);//通过比较器查找
if (key == null)
throw new NullPointerException();
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;
}
//删除指定键值对节点。
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的后继节点s
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children
// replacement节点指向后继节点s的左孩子或右孩子
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) {
// 将replacement节点链接到原来后继节点的父节点上
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;
}
}
}
TreeMap节点查找
//根据键来获取值 ,时间复杂度是红黑树的时间复杂度O(logn)
public V get(Object key) {
Entry<K,V> p = getEntry(key);//上面说过了
return (p==null ? null : p.value);
}
//获取最小的键值对
final Entry<K,V> getFirstEntry() {
Entry<K,V> p = root;
if (p != null)
while (p.left != null)
p = p.left;
return p;
}
//获取最大的键值对
final Entry<K,V> getLastEntry() {
Entry<K,V> p = root;
if (p != null)
while (p.right != null)
p = p.right;
return p;
}
TreeMap的迭代查找
说到迭代查找,那肯定就少不了迭代接口Iterable,这里顺便说一下foreach用法
(1)遍历数组
(2)foreach用于遍历任何实现Iterable接口对象
public interface Iterable<T> {
Iterator<T> iterator();
}
而TreeMap重写了父类AbstractMap的entrySet()方法。
public Set<Map.Entry<K,V>> entrySet() {
EntrySet es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet());
}
而EntrySet从其父类间接继承了Iterable接口。
class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator(getFirstEntry());//getFirstEntry获取第一个(有序的)
//键值对节点
}
//省略其他方法。。。
}
final class EntryIterator extends PrivateEntryIterator<Map.Entry<K,V>> {
EntryIterator(Entry<K,V> first) {
super(first);
}
public Map.Entry<K,V> next() {
return nextEntry();
}
}
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);//successor前面说过,获取有序的下一个键值对节点。
lastReturned = e;
return e;
}
测试代码如下:
public static void main(String[] args) {
TreeMap<Integer,String> m=new TreeMap<Integer,String> ();
m.put(5, "555");
m.put(1, "111");
m.put(3, "333");
m.put(4, "444");
m.keySet().iterator();
Iterator<Map.Entry<Integer, String>> it= m.entrySet().iterator();
while(it.hasNext()){
it.next();
}
for(Map.Entry<Integer, String> entry:m.entrySet()){
System.out.println(entry.getKey()+","+entry.getValue());
}
}
输出结果为:
1,111
3,333
4,444
5,555