关于TreeMap你忽略了的那些
什么是TreeMap呢?
TreeMap,是从名字就可以看出来,是用树结构来实现Map映射的,它的底层实现用的是红黑树,从Java官方的TreeMap的部分代码就可以看得出来(有给节点染色):
private void fixAfterDeletion(Entry<K,V> x)
方法(用于恢复树的平衡):
/** From CLR */
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
Entry<K,V> sib = rightOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else { // symmetric
Entry<K,V> sib = leftOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}
if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}
setColor(x, BLACK);
}
你知道TreeMap的用途吗?
在说它的用途之前,就要先说一下它的性质了,它的性质其实和红黑树的性质几乎一样,因为它是用红黑树的每一个节点来存储一个Entry(即一个键值对)
,除此之外我们还知道,红黑树其实也是属于平衡二叉搜索树,既然如此,在进行插入节点的时候必然要对节点进行比较,如果相同则覆盖,一次来保证红黑树上每一个节点存储的值都是不一样的
,众所周知的是Map中的Key都是唯一不重复的,因此综上所述,红黑树它在进行节点比较的时候,其实比较的是每一个Entry的Key。
小小总结一下:
- 由红黑树实现,Key不重复且
- 红黑树上的每一个Node都是一个Entry(即K-V对)
- 为了保证红黑树的平衡,必须保证存储的Key是可比较的
在知道了上面的知识后,我们再想想我们知道的 Set
的数据结构,它存储的值也都是不重复的,是不是和我们上面说到的TreeMap的部分性质有点像呢?
其实Java中的TreeSet就是用TreeMap实现的!下面来看看它的部分源码:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
private transient NavigableMap<E,Object> m;
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
public TreeSet() {
this(new TreeMap<E,Object>());
}
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
//后面的代码省略....
}
我们简单来看一下,从构造方法可以看出来,在初始化一个TreeSet的时候,其实是在初始化一个NavigableMap
,而它其实就是TreeMap了。其实TreeSet是通过TreeMap将值存入到Key中实现的,所有的Value都是null。
为什么有了TreeMap,还要HashMap呢?
相信这个问题是被人忽略做多的了,几乎没多少人去仔细想过,因为在探究为什么之前,需要你对红黑树以及哈希表具备一定的了解,但是本文想带你通过阅读文章来进行此问题的探究,因此,本篇会先告诉你HashMap诞生的缘由,以及TreeMap有哪些局限性,并由此为讲解HashMap开个头,好了,话不多说直接进入正题!
-
TreeMap的局限性
之前在说TreeMap的性质的时候,有说到过K的可
比较性
,这其实便是TreeMap的局限所在,因为很多时候我们存储的数据并不具备可比较性,因此在进行存储的时候,那些进行比较的操纵其实就有点浪费性能了 -
为什么需要HashMap
知道了TreeMap的局限性之后,你大概就能猜出来HashMap为何诞生了的吧。其实HashMap是一种不考虑Key的可比较性的数据,并且它相比较TreeMap的O(log(n))的时间复杂度,可以达到O(1)级别,它的底层是采用
哈希表
来实现的
在今后的文章中会对HashMap进行详细的讲解