TreeMap同样属于Map集合中的一员,但是它没有用到散列表和链表这些数据结构,它只使用了红黑树这一数据结构,可以直接在红黑树中进行增删改查操作。我们首先回顾一下Map集合的家族成员:
可以看出TreeMap是和HashMap并列的,而HashMap中的键值对是无序的,TreeMap中的键值对是按照一定的顺序进行排序的。
接下来继续看其继承结构:
public class TreeMap<K,V> extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
我们在继承体系中看到了一个陌生的接口“NavigableMap”,这个接口有什么用呢?
public interface NavigableMap<K,V> extends SortedMap<K,V>{
Map.Entry<K,V> lowerEntry(K key);
//获得小于key值的最大Entry
K lowerKey(K key);
//获得小于key值的最大Key
Map.Entry<K,V> floorEntry(K key);
//获得不大于key值的最大Entry
K floorKey(K key);
//获得不大于key值的最大Key
Map.Entry<K,V> ceilingEntry(K key);
//获得不小于key值的最小Entry
K ceilingKey(K key);
//获得不小于key值的最小Key
Map.Entry<K,V> higherEntry(K key);
//获得大于key值的最小Entry
K higherKey(K key);
//获得大于key值的最小key
Map.Entry<K,V> firstEntry();
//获得第一个Entry
Map.Entry<K,V> lastEntry();
//获得最后一个Entry
Map.Entry<K,V> pollFirstEntry();
//获得并删除第一个Entry
Map.Entry<K,V> pollLastEntry();
//获得并删除最后一个Entry
NavigableMap<K,V> descendingMap();
//获得逆序的Map
NavigableSet<K> navigableKeySet();
//获得顺序的key值集合
NavigableSet<K> descendingKeySet();
//获得逆序的key值集合
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive);
//获得子Map
NavigableMap<K,V> headMap(K toKey, boolean inclusive);
//获得首部子Map
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
//获得尾部子Map
SortedMap<K,V> subMap(K fromKey, K toKey);
//获得SortedMap类型的子Map
SortedMap<K,V> headMap(K toKey);
//获得SotredMap类型的首部子Map
SortedMap<K,V> tailMap(K fromKey);
//获得SortedMap类型的尾部子Map
}
public interface SortedMap<K,V> extends Map<K,V> {
Comparator<? super K> comparator();
//比较器
SortedMap<K,V> subMap(K fromKey, K toKey);
//获得子Map
SortedMap<K,V> headMap(K toKey);
//获得首部子Map
SortedMap<K,V> tailMap(K fromKey);
//获得尾部子Map
K firstKey();
//获取第一个Key值
K lastKey();
//获取最后一个key值
Set<K> keySet();
//获得所有的Key组成的集合
Collection<V> values();
//获得所有的value组成的集合
Set<Map.Entry<K, V>> entrySet();
//获得所有的Entry组成的集合
}
由上面源码可知,SortedMap继承自Map接口,并且新增了比较器方法、获取首尾key方法、获得子Map方法与获得key、value、Entry集合的方法,而NavigableMap接口除了继承SortedMap类中的方法之外,还新增了一系列获取边界的key和Entry结点的方法。总之,就是除了有Map接口的方法之外,还新增了一个排序的功能。
接下来看看它的成员变量:
private final Comparator<? super K> comparator;
//自定义比较器
private transient Entry<K,V> root;
//根节点,不可序列化
private transient int size = 0;
//结点个数,不可序列化
private transient int modCount = 0;
//修改次数,不可序列化
static final class Entry<K,V> implements Map.Entry<K,V>{
//红黑树结点类
//成员变量
K key;//key值
V value;//value值
Entry<K,V> left;//左孩子
Entry<K,V> right;//右孩子
Entry<K,V> parent;//父结点
boolean color = BLACK;//默认黑色结点
}
由上可知,TreeMap的底层数据结构就是红黑树,并可以通过自己设置比较器来构建相应的红黑树。
接下来研究一下其构造器:
public TreeMap(){
//构造器1,不定义比较器
comparator = null;
}
public TreeMap(Comparator<? super K> comparator){
//构造器2,传入构造器,根据构造器建树
this.comparator = comparator;
}
public TreeMap(Map<? extends K, ? extends V> m){
//构造器3,传入普通Map类型的容器,将容器中的键值对加入到树中
comparator = null;
//将比较器设置为Null
putAll(m);
}
public TreeMap(SortedMap<K, ? extends V> m){
//构造器4,传入的是一个排序的Map类型容器,将容器中的键值对按照顺序
//加入到树中
comparator = m.comparator();
//将m的比较器设置成本类的比较器
try{
//使用迭代的方法将m的键值对复制到新的TreeMap的容器中
buildFromSorted(m.size(), m.entrySet().iterator(),null,null)
}catch(java.io.IOException cannotHappen){
}catch(ClassNotFoundException cannotHappen){
}
}
由上可知,TreeMap有四个构造器,当参数为空时,则直接将比较器设置为空,加入键值对构造红黑树时就利用键自己的比较器进行比较;当参数为比较器时,则将比较器设置为传入的比较器,加入键值对构造红黑树时就采用传入的比较器;当参数为Map集合时,就直接将比较器置空,再将map中的键值对依次插入到红黑树中;当参数为SortedMap集合时,则获取其比较器作为自己的比较器,再将其键值对依次插入到红黑树中。
下面再来看看常用方法:
1.put
public V put(K key, V value){
//将一个键值对放入红黑树中
Entry<K,V> t = root;
//获取红黑树的根
if(t == null){
//如果根为空,则说明红黑树中没有结点
compare(key,key);//类型验证
root = new Entry<>(key, value, null);
//新建结点作为根
size = 1;
//结点数量设置为1
modCount++;
//修改次数
return null;
//插入成功则返回null
}
int cmp;
//标记,记录新节点应该插在哪里或者寻找插入点时下一次向什么方向寻找
Entry<K,V> parent;
Comparator<? super K> cpr = comparator;
//获得比较器
if(cpr != null){
//如果比较器不为空,则用TreeMap中的比较器进行比较
do{
parent = t;
//将当前结点作为父结点
cmp = cpr.compare(key, t.key);
//获得比较结果
if(cmp < 0)
//如果传入的key值比当前结点的key值小
t = t.left;
//则向其左子树下找
else if(cmp > 0)
//如果传入key值比当前结点的key值大
t = t.right;
//则向其右子树下找
else
return t.setValue(value);
//如果相等,则将该结点的value值设置为传进来的value值
//从此处可以看出其和HashMap的不同,key值不允许有相同的
}while(t != null);
}
else{
//否则表示比较器为空,用key类自己的比较方式进行比较
if(key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>)key;
//对传进来的key进行强制类型转换
do{
//进行比较,寻找插入点
parent = t;
cmp = k.compareTo(t.key);
if(cmp < 0)
t = t.left;
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)
//如果cmp小于零,则表示将e插入其父结点的左孩子处
parent.left = e;
else
parent.right = e;
//否则将e插入其父节点的右孩子处
fixAfterInsertion(e);
//插入后的调整,类似HashMap中的balanceInsertion方法
size++;
//结点数目加一
modCount++;
//修改次数加一
return null;
//插入成功,返回Null
}
插入操作即直接在红黑树中操作,先找到需要插入的位置,如果该位置中已经有键值对了,则将该键值对更新成传进去的value值并返回新的value值,否则直接将键值对插入并且开始调整红黑树。
2.get
public V get(Object key){
//根据key寻找相应键值对,如果没找到返回null,
//找到了则返回key对应的value值
Entry<K,V> p = getEntry(key);
return (p == null ? null : p.value);
}
final Entry<K,V> getEntry(Object key){
//根据key值获得键值对
if(comparator != null)
//如果比较器不为空,则调用使用比较器的方法
return getEntryUsingComparator(key);
if(key == null)
//如果key值为空,则抛出空指针异常
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>)key;
//如果没有自定义比较器,则按照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;
}
get方法即首先判断有没有比较器,如果没有比较器则按照key值自己的比较器进行比较,寻找对应的红黑树结点并返回其value值,如果value值为空或者没有找到对应的键值对时则返回null。
3.remove
public V remove(Object key){
//根据传入的key值删除对应的键值对
Entry<K,V> p = getEntry(key);
//先找到该键值对
if(p == null)
return null;
//如果找不到,则返回Null
V oldValue = p.value;
//如果找到该结点,则先得到其value值
deleteEnrty(p);
//删除该结点
return oldValue;
//返回被删除结点的value值
}
remove方法首先通过key获得相应的结点,如果找不到结点则返回null,否则先将结点的value值取出,再将结点删除,最后返回被删除结点的value值。
4.获得边界结点
public Map.Entry<K,V> firstEntry(){
//获得第一个键值对
return exportEntry(getFirstEntry());
//返回调用exportEntry方法
}
public Map.Entry<K,V> lastEntry(){
//获得最后一个键值对
return exportEntry(getLastEntry());
}
public Map.Entry<K,V> pollFirstEntry(){
//删除并返回第一个结点
Entry<K,V> p = getFirstEntry();
//获得第一个结点
Map.Entry<K,V> result = exportEntry(p);
//返回p的一个简单拷贝结点
if(p != null)
deleteEntry(p);
//删除找到的结点
return result;
//返回结果
}
public Map.Entry<K,V> pollLastEntry(){
//删除并返回最后一个结点
Entry<K,V> p = getLastEntry();
Map.Entry<K,V> result = exportEntry(p);
if(p != null)
deleteEnrty(p);
return result;
}
5.获取相应集合
public Set<K> keySet(){
//获取遍历TreeMap中key的Set容器
return navigableKeySet();
}
public NavigableSet<K> navigableKeySet(){
//获取顺序的Set容器的key集合
KeySet<K> nks = navigableKeySet;
return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this));
}
public NavigableSet<K> descendingKeySet(){
//返回逆序的Set容器的key集合
return descendingMap().navigableKeySet();
}
public Collection<V> values(){
//返回包含TreeMap中的所有value的集合
Collection<V> vs = values;
if(vs == null){
vs = new Values();
values = vs;
}
return vs;
}
public Set<Map.Entry<K,V>> entrySet(){
//获取包含TreeMap中的所有键值对集合的Set容器
EntrySet es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet());
}
6.其他方法
public void clear(){
//清空红黑树
modCount++;
//修改次数加一
size = 0;
//将结点数目重置为0
root = null;
//将根节点置为空就够了
}
public int size(){
//获得树中的结点数量
return size;
}
public boolean containsKey(Object key){
//判断树中是否存在含有key值的结点
return getEntry(key) != null;
}
public boolean containsValue(Object value){
//判断树中是否存在含有value值的结点
for(Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
//遍历树中的结点,一个个进行比较,存在则返回true,不存在则返回false
if(valEquals(value,e.value))
return true;
return false;
}
总结:TreeMap的底层数据结构是红黑树,可以通过自定义比较器的方式让TreeMap中的键值对按照相应的方式进行排序,并可以通过顺序或逆序对TreeMap中的键值对进行遍历。除此之外还可以获得TreeMap中的子Map,即红黑树中的子树,并对其进行操作。
总源码
public class TreeMap<K,V> extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
private final Comparator<? super K> comparator;
//自定义比较器
private transient Entry<K,V> root;
//根节点,不可序列化
private transient int size = 0;
//结点个数,不可序列化
private transient int modCount = 0;
//修改次数,不可序列化
public TreeMap(){
//构造器1,不定义比较器
comparator = null;
}
public TreeMap(Comparator<? super K> comparator){
//构造器2,传入构造器,根据构造器建树
this.comparator = comparator;
}
public TreeMap(Map<? extends K, ? extends V> m){
//构造器3,传入普通Map类型的容器,将容器中的键值对加入到树中
comparator = null;
//将比较器设置为Null
putAll(m);
}
public TreeMap(SortedMap<K, ? extends V> m){
//构造器4,传入的是一个排序的Map类型容器,将容器中的键值对按照顺序
//加入到树中
comparator = m.comparator();
//将m的比较器设置成本类的比较器
try{
//使用迭代的方法将m的键值对复制到新的TreeMap的容器中
buildFromSorted(m.size(), m.entrySet().iterator(),null,null)
}catch(java.io.IOException cannotHappen){
}catch(ClassNotFoundException cannotHappen){
}
}
public int size(){
//获得树中的结点数量
return size;
}
public boolean containsKey(Object key){
//判断树中是否存在含有key值的结点
return getEntry(key) != null;
}
public boolean containsValue(Object value){
//判断树中是否存在含有value值的结点
for(Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
//遍历树中的结点,一个个进行比较,存在则返回true,不存在则返回false
if(valEquals(value,e.value))
return true;
return false;
}
public V get(Object key){
//根据key寻找相应键值对,如果没找到返回null,
//找到了则返回key对应的value值
Entry<K,V> p = getEntry(key);
return (p == null ? null : p.value);
}
public Comparator<? super K> comparator(){
//获得树中的比较器
return comparator;
}
public K firstKey(){
//获得第一个结点的key值
return key(getFirstEntry());
}
public K lastKey(){
//获得最后一个结点的Key值
return key(getLastEntry());
}
public void putAll(Map<? extends K, ? extends V> map){
//将Map类型容器中的键值对放入本容器中
int mapSize = map.size();
//获得m中的结点数量
if(size == 0 && mapSize != 0 && map instanceof SortedMap){
//如果当前容器中没有结点且传进来的容器有结点并是SortedMap类型
//的容器
Comparator<?> c = ((SortedMap<?,?>)map).comparator();
//获得map中的比较器
if(c == comparator || (c != null && c.equals(comparator))){
//如果两者的比较器一样,则开始复制结点
++modCount;
try{
buildFromSorted(mapSize, map.entrySet().iterator(), null, null);
}catch(java.io.IOException cannotHappen){
}catch(ClassNotFoundException cannotHappen){
}
return;
}
}
super.putAll(map);
//如果不满足,则调用父类的方法进行加入
}
final Entry<K,V> getEntry(Object key){
//根据key值获得键值对
if(comparator != null)
//如果比较器不为空,则调用使用比较器的方法
return getEntryUsingComparator(key);
if(key == null)
//如果key值为空,则抛出空指针异常
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>)key;
//如果没有自定义比较器,则按照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;
}
final Entry<K,V> getEntryUsingComparator(Object key){
//按照比较器进行查询,根据key值获得相应的键值对
@Suppressings("unchecked")
K k = (K) key;
//获得传入的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;
//如果找不到,则返回null
}
final Entry<K,V> getCeilingEntry(K key){
//寻找不小于key的最小结点,也就是说要么是它自己
//要么是红黑树中序遍历中最接近自己的且比自己大的结点
Entry<K,V> p = root;
//首先获得根节点
while(p != null){
//开始深度遍历,使用比较器
int cmp = compare(key, p.key);
if(cmp < 0){
//如果传进来的key比当前结点的key小,向左孩子深入
if(p.left != null)
p = p.left;
else
//如果左孩子为空,说明此结点已经是这棵树中大于key
//中结点的最小结点了
return p;
}else if(cmp > 0){
//如果传进来的key比当前结点的key值大,则向右孩子深入
if(p.right != null){
p = p.right;
}else{
//如果右孩子为空,则要找到中序遍历的后一个结点,也就是说
//如果在遍历的路径上有一条路是通过深入父结点的左孩子下来的
//则就找那最近的父结点,如果没有,说明是一直是通过深入父结点的
//右孩子下来的,则一直追溯到根节点,返回null
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;
//否则返回null
}
final Entry<K,V> getFloorEntry(K key){
//和上一个方法相反,找到不大于key的最大结点,如果没有,则返回Null
Entry<K,V> p = root;
while(p != null){
int cmp = compare(key, p.key);
if(cmp > 0){
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.parnet;
}
return parent;
}
}else
return p;
}
return null;
}
final Entry<K,V> getHigherEntry(K key){
//获得大于key值的最小结点,自己不算,和getCeilingEntry方法类似
Entry<K,V> p = root;
while(p != null){
int cmp = compare(key, p.key);
if(cmp < 0){
if(p.left != null)
p = p.left;
else
return p;
}else{
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;
}
}
}
return null;
}
final Entry<K,V> getLowerEntry(K key){
//获得小于key值的最大结点,自己不算,和getFloorEntry方法类似
Entry<K,V> p