本文基于jdk1.8
介绍TreeMap
。
TreeMap
在上一篇文章Java集合框架之LinkedHashMap详解中, 我们介绍了Java
集合框架的一个类LinkedHashMap
。今天我们来介绍一下java
集合框架中的TreeMap
,先回顾一下map
接口的类图:
基本特性
从类图中我们可以看到,TreeMap
继承自AbstractMap
,并实现了NavigableMap
、Cloneable
和Serializable
接口,AbstractMap
完成了大部分map
接口所支持的方法,而NavigableMap
接口继承自SortedMap
接口,SortedMap
存储的是有序的键值对,他内部维护了一个Comparator
比较器,NavigableMap
是一个可导航的键-值对集合,具有了为给定搜索目标报告最接近匹配项的导航方法。所以可以总结TreeMap
有以下特性:
1、排序性
2、可以被克隆
3、可以被序列化
与LinkedHashMap
一样,TreeMap
也保证了集合元素的顺序性,下面我们就来看一下它是如何实现顺序性的。
存储结构
分析一个数据类型最好的办法就是看它的内部存储结构,通过分析存储的数据结构,我们可以更清楚的看到它的实现原理和方法,从而更好的去使用,因为特定的数据结构只有用在特定的场景下,才会发挥它最大的作用。TreeMap
内部使用的数据结构为红黑树(Red-Black tree),关于红黑树,这里简单介绍一下,红黑树属于二叉排序树,但是在二叉排序树的基础上,又增加了一些规则,比如定义节点的着色等,这样就不会出现一些极端的情况,比如,整个树出现了偏离,变为了单分支结构的树,这样,整个树的高度就是n,n为全部的节点数。在这样的节点分部情况下,查找一个节点所需的时间复杂度为O(n)
,为了避免这样的情况,聪明的人类又发明了另一种树形结构,叫做平衡二叉树,平衡二叉树查找、插入和删除在平均和最坏情况下的时间复杂度都是 O(logn)
。红黑树就是平衡二叉树的一种实现方式。红黑树定义了一些规则,保证树的高度维持在logn
。
1、每个结点要么是红的要么是黑的。
2、根结点是黑的。
3、每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
4、如果一个结点是红的,那么它的两个儿子都是黑的。
5、对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。
按照上述性质我们可以很容易的构造一棵红黑树,但是当在对红黑树进行插入和删除等操作时,对树做了修改可能会破坏红黑树的性质。为了继续保持红黑树的性质,可以通过对结点进行重新着色,以及对树进行相关的旋转操作,即通过修改树中某些结点的颜色及指针结构,来达到对红黑树进行插入或删除结点等操作后继续保持它的性质或平衡的目的。这里有三个比较重要的操作,左旋、右旋和着色,着色比较好理解,黑色或者红色,我们重点看一下左旋和右旋。
左旋
如上图所示,当需要将pivot
节点左旋时,我们假设它的右孩子y不是NIL[T]
,pivot
可以为任何不是NIL[T]
的左子结点。左旋以pivot
到y之间的链为“支轴”进行,它使y成为该子树的新根,而y的左孩子b则成为pivot的右孩子。
右旋
如上图所示,当需要将pivot
节点右旋时,我们假设它的左孩子y不是NIL[T]
,pivot可以为任何不是NIL[T]
的右子结点。右旋以pivot
到y之间的链为“支轴”进行,它使y成为该子树的新根,而y的左孩子b则成为pivot
的右孩子。
关于红黑树就说到这里,感兴趣的同学可以看一下July大神的博文教你初步了解红黑树,下面我们结合源码来看一下TreeMap
是怎么样利用这一结构实现的。
几个基本属性
|
|
通过以上代码我们可以看到,TreeMap
内部维护着一个比较器comparator
,通过这个比较器,可以实现根据key
的排序。root
是TreeMap
存储元素的根节点,这个根节点的类型为Entry<K,V>
,是基于红黑树实现的一个数据结构,下面是部分源码,我们可以看到这个Entry<K,V>
直接继承了Map.Entry<K,V>
,除了基本的key
和vlaue
属性,还多了其他属性。left
表示红黑树结构的左子树节点的引用,right
表示右子树几点的引用,parent
表示父节点的引用,还有一个color
表示当前节点的颜色,这是一个布尔类型的值,通过设置true
或者false
来表示黑色或者红色。
|
|
构造方法
|
|
TreeMap
提供了四个构造方法,默认的无参构造方法实例化一个key
的自然序列的顺序,要求key
必须实现Comparable
接口。同时还提供了一个指定比较器的构造方法,用以使用指定比较器实现排序。
添加元素
将一个节点添加到红黑树中,通常需要下面几个步骤:
1、将红黑树当成普通二叉查找树,将节点插入。
2、将新插入的节点设置为红色。
3、通过旋转和着色,使它恢复平衡,重新变成一颗符合规则的红黑树。
下面我们重点看一下添加元素的实现方式:
|
|
fixAfterInsertion
方法
总结
1、基于红黑树(Red-Black tree
)的数据结构实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator
进行排序,具体取决于使用的构造方法。
2、不允许插入为Null
的key
3、可以插入值为Null
的Value
4、若Key
重复,则后面插入的直接覆盖原来的Value
5、非线程安全
6、根据key
排序,key
必须实现Comparable
接口,可自定义比较器实现排序。
7、TreeMap
使用的数据结构决定了他的插入操作变的比较复杂,需要维护一个红黑树,所以TreeMap
不适合用在频繁修改的场景,如果不需要实现有序性,则建议使用HashMap
,存取效率要高一些。
参考
http://www.jianshu.com/p/fc5e16b5c674
http://blog.csdn.net/v_july_v/article/details/6105630
https://zh.wikipedia.org/wiki/AVL%E6%A0%91
https://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91