TreeMap底层是使用红黑树实现的存储键值对的map容器,可以通过比较器进行排序。红黑树本质上是一棵弱平衡二叉树,它的节点有红色和黑色两种颜色。主要特性有五点。
(1)每个节点或者是黑色,或者是红色。(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
TreeMap中重要参数及结构。
/**
* treemap中包含一个比较器,可以用来定义顺序
*/
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;
/**
* 树中键值对的个数
*/
private transient int size = 0;
/**
* 用于快速失败,记录修改次数,不是线程安全
*/
private transient int modCount = 0;
/**
* 构造一个空树,比较器为空,key值按照自然顺序排序。
*/
public TreeMap() {
comparator = null;
}
/**
* 构造一个空树,根据所给的比较器进行排序 ,所有key值在插入时都要使用比较器。
*/
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
/**
* 使用所给的map进行插入,key值按照自然顺序进行排序。
*/
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
/**
* 构造一棵和排序树相同顺序的树。键值对一样,比较器相等。
*/
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) {
}
}
查找。根据key值来进行查找,从根上开始,依次进行遍历。
/**
* 根据所给的key值进行查找,首先看比较器是否空,然后判断Key是否为null,然后根据自然顺序查找。
*/
final Entry<K,V> getEntry(Object key) {
//比较器不为空需要使用比较器进行查找
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)//key值为空直接抛出异常
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)//k小于p,p往左分支上走(左分支小于当前root)
p = p.left;
else if (cmp > 0)//k大于p,p往右分支上走
p = p.right;
else//找到返回p
return p;
}
return null;//没找到返回null
}
/**
* 使用比较器的版本
*/
final Entry<K,V> getEntryUsingComparator(Object key) {
@SuppressWarnings("unchecked")
K k = (K) key;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
Entry<K,V> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
}
return null;
}
/**
* 获取大于等于key的最小节点
*/
final Entry<K,V> getCeilingEntry(K key) {
Entry<K,V> p = root;
while (p != null) {
int cmp = compare(key, p.key);
if (cmp < 0) {//如果key值比较小,应该往左分支上去找
if (p.left != null)
p = p.left;
else//找的了则返回
return p;
} else if (cmp > 0) {//如果key值比较大,则首先往右分支上去找
if (p.right != null) {
p = p.right;
} else {//如果已经没有右节点。
Entry<K,V> parent = p.parent;
Entry<K,V> ch = p;
while (parent != null && ch == parent.right) {
ch = parent;
parent = parent.parent;
}
return parent;
}
} else
return p;
}
return null;
}
/**
* 获取小于等于key最大的节点
*/
final Entry<K,V> getFloorEntry(K key) {
Entry<K,V> p = root;
while (p != null) {
int cmp = compare(key, p.key);
if (cmp > 0) {//如果key大于p,则p变大,一直到最右节点。
if (p.right != null)
p = p.right;
else
return p;
} else if (cmp < 0) {
if (p.left != null) {
p = p.left;
} else {
Entry<K,V> parent = p.parent;
Entry<K,V> ch = p;
while (parent != null && ch == parent.left) {
ch = parent;
parent = parent.parent;
}
return parent;
}
} else
return p;
}
return 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;
// 根据是否有比较器进行再次判断。
Comparator<? super K> cpr = comparator;
if (cpr != null) {//比较器不为空
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)//如果key比t小,那么应该插在t的左子树上
t = t.left;
else if (cmp > 0)//反之,则应该插在右子树上
t = t.right;
else//如果key相等,那么这就是个修改操作。
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)//如果key比较小,则插入t的左子树上
t = t.left;
else if (cmp > 0)//如果key比较大,则插入t的右子树上
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);//如果不是修改的话,那么就把这个e放在相应的树分支上即可。
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);//调整树结构,满足红黑树的性质。
size++;
modCount++;
return null;
}
删除一个节点。
/**
* 删除一个节点,然后重新使树变的平衡。
*/
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--;
// 如果p有左子树和右子树
if (p.left != null && p.right != null) {
Entry<K,V> s = successor(p);//找到p的下一个节点
p.key = s.key;
p.value = s.value;
p = s;//将p的下一个节点给p。
}
// 记录p的替代节点。也就是p的左节点,如果为空则是右节点。
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) {//替代节点不为空的情况下,将替换节点作为p的父节点的子节点。
// 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;
// 删除p节点
p.left = p.right = p.parent = null;
// 删除之后修复
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) { //唯一节点被删除,置root为null.
root = null;
} else { // 没有子节点同时也不是根的情况
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> 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;//从t的右子树分支上找到最左分支
while (p.left != null)
p = p.left;
return p;
} else {//t的右子树为空时,沿父节点找到第一个非右节点。
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
调整树结构,左旋右旋操作。
//对一个节点进行左旋操作
private void rotateLeft(Entry<K,V> p) {
if (p != null) {
Entry<K,V> r = p.right;
p.right = r.left;
if (r.left != null)
r.left.parent = p;
r.parent = p.parent;
if (p.parent == null)
root = r;
else if (p.parent.left == p)
p.parent.left = r;
else
p.parent.right = r;
r.left = p;
p.parent = r;
}
}
//对一个节点进行右旋操作
private void rotateRight(Entry<K,V> p) {
if (p != null) {
Entry<K,V> l = p.left;
p.left = l.right;
if (l.right != null) l.right.parent = p;
l.parent = p.parent;
if (p.parent == null)
root = l;
else if (p.parent.right == p)
p.parent.right = l;
else p.parent.left = l;
l.right = p;
p.parent = l;
}
}
//插入一个节点后保证红黑树依旧是红黑树。
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
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 {
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;
}
TreeMap的使用:常用的方法示例。
TreeMap<Integer,String> map=new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});//构建一个TreeMap,key值从小到大排序
map.put(3,"22");
map.put(2,"333");
map.put(5,"333");
map.put(2,"77777");//向TreeMap中添加键值对
System.out.println(map.toString());
System.out.println(map.ceilingKey(4));//大于等于key值为4的下一个key
System.out.println(map.floorKey(4));//小于等于key值为4 的下一个key
System.out.println(map.containsKey(1));//是否包含key值为1的键值对
System.out.println(map.keySet());
System.out.println(map.firstKey());//最开始的一个键值对
System.out.println(map.lastEntry());//最后的一个键值对
System.out.println(map.descendingKeySet());//逆序key
System.out.println(map.get(2));//获取key为2的value
System.out.println(map.higherKey(3));//key大于3的下一个key
参考博客:http://www.cnblogs.com/skywang12345/p/3310928.html
https://blog.csdn.net/ns_code/article/details/36421085
https://juejin.im/entry/57bfab077db2a20068ebf9d2
http://www.cnblogs.com/xrq730/p/6867924.html(红黑树的概念)
http://www.cnblogs.com/skywang12345/p/3245399.html(红黑树左旋右旋)