java1.8 常用集合源码学习:TreeMap

1、api
基于红黑树(Red-Black tree)的  NavigableMap  实现。该映射根据其键的 自然顺序 进行排序,或者根据创建映射时提供的  Comparator  进行排序,具体取决于使用的构造方法。
此实现为  containsKey get put  和  remove  操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的  Introduction to Algorithms  中的算法的改编。
注意,如果要正确实现  Map  接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须 与 equals 一致 。(关于 与 equals 一致  的精确定义,请参阅  Comparable  或  Comparator )。这是因为  Map  接口是按照 equals 操作定义的,但有序映射使用它的  compareTo (或  compare )方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使排序与 equals 不一致,有序映射的行为仍然  定义良好的,只不过没有遵守  Map  接口的常规协定。
注意,此实现不是同步的。 如果多个线程同时访问一个映射,并且其中至少一个线程从结构上修改了该映射,则其 必须  外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与现有键关联的值不是结构上的修改。)这一般是通过对自然封装该映射的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用  Collections.synchronizedSortedMap  方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:
SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));
collection(由此类所有的“collection 视图方法”返回)的  iterator  方法返回的迭代器都是 快速失败  的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的  remove  方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出  ConcurrentModificationException 。因此,对于并发的修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,一般来说,当存在不同步的并发修改时,不可能作出任何肯定的保证。快速失败迭代器尽最大努力抛出  ConcurrentModificationException 。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。
此类及其视图中的方法返回的所有  Map.Entry  对都表示生成它们时的映射关系的快照。它们  支持  Entry.setValue  方法。(不过要注意的是,使用  put  更改相关映射中的映射关系是有可能的。)
此类是  Java Collections Framework  的成员。

2、源码学习

比较器
private final Comparator super K > comparator ;

红黑树的根节点
private transient Entry< K , V > root ;

结构修改的次数
private transient int modCount = 0 ;

从已有map创建TreeMap,调用了putAll方法
public TreeMap (Map extends K , ? extends V > m) {
comparator = null;
putAll(m) ;
}

用SortedMap创建TreeMap,调用了buildFromSorted方法
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,调用了getEntry方法
public boolean containsKey (Object key) {
return getEntry(key) != null;
}

取得指定key的value
final Entry< K , V > getEntry (Object key) {
// Offload comparator-based version for sake of performance
// 如果有比较器,则使用 getEntryUsingComparator 方法
if ( comparator != null )
return getEntryUsingComparator(key) ;
// 不允许 key null
if (key == null )
throw new NullPointerException() ;
@SuppressWarnings ( "unchecked" )
Comparable super K > k = (Comparable super K >) key ;
Entry< K , V > p = root ;
while (p != null ) {
// 比较待查找 key 和当前节点 p (第一次进入循环是当前节点为根节点)的 key
int cmp = k.compareTo(p. key ) ;
// 如果待查找的 key 小于当前节点 key ,则将 p 置位 p 的左节点,继续查找
if (cmp < 0 )
p = p. left ;
// 如果待查找的 key 大于当前节点 key ,则将 p 置位 p 的右节点,继续查找
else if (cmp > 0 )
p = p. right ;
// 如果两个 key 相等,则返回当前节点 p
else
return p ;
}
// 如果没找到返回 null
return null;
}

这个方法是getEntry方法的带比较器的版本,实现基本是一样的,也是从根节点开始找
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;
}

查看map是否包含给定的value
public boolean containsValue (Object value) {
// 首先调用 getFirstEntry 取得红黑树最左侧的子节点,然后遍历时每次调用 successor 取得下一个元素
for (Entry< K , V > e = getFirstEntry() ; e != null; e = successor (e))
// 调用 valEquals 判断两个值是否相等
if ( valEquals (value , e. value ))
return true;
return false;
}

getFirstEntry方法取得整个树的最左侧的子节点
final Entry< K , V > getFirstEntry () {
Entry< K , V > p = root ;
if (p != null )
while (p. left != null )
p = p. left ;
return p ;
}

successor方法查找比给定的节点t大的节点
predecessor方法查找比给定的节点t小的节点,实现方式和successor非常类似,只是方向相反,不再赘述
static < K , V > TreeMap.Entry< K , V > successor (Entry< K , V > t) {
// 如果 t null 直接返回 null
if (t == null )
return null;
// 如果 t 有右子树,则取得 t 的右子树的最左侧的子节点返回
else if (t. right != null ) {
Entry< K , V > p = t. right ;
while (p. left != null )
p = p. left ;
return p ;
// 如果 t 没有右子树(即 t 是一个叶子节点),则判断 t 本身,如果 t 为它父亲的左节点,则返回t的父节点,如果 t 为它父亲的右节点,则往上推,直到他的某一层的祖先是其父亲的左子节点,返回这个祖先的父节点
} else {
Entry< K , V > p = t. parent ;
Entry< K , V > ch = t ;
while (p != null && ch == p. right ) {
ch = p ;
p = p. parent ;
}
return p ;
}
}

valEquals方法用于判断两个给定对象是否相等(或者都为null)
static final boolean valEquals (Object o1 , Object o2) {
return (o1== null ? o2== null : o1.equals(o2)) ;
}

取得map的最后一个key,调用了getLastEntry方法和key方法
public K lastKey () {
return key (getLastEntry()) ;
}

getLastEntry方法取得了map的最右侧的子节点
final Entry< K , V > getLastEntry () {
Entry< K , V > p = root ;
if (p != null )
while (p. right != null )
p = p. right ;
return p ;
}

key方法取得了给定的键值对的键对象
static < K > K key (Entry< K , ?> e) {
if (e== null )
throw new NoSuchElementException() ;
return e. key ;
}

将给定的map的所有键值对都put到当前TreeMap中,如果给定的map是SortedMap并且比较器和当前TreeMap相同,则调用buildFromSorted方法,否则直接调用super.putAll(遍历给定map,调用TreeMap的put方法)
public void putAll (Map extends K , ? extends V > map) {
int mapSize = map.size() ;
if ( size == 0 && mapSize!= 0 && map instanceof SortedMap) {
Comparator c = ((SortedMap , ?>)map).comparator() ;
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) ;
}

getCeilingEntry方法返回大于或等于给定key的键值对(优先返回等于的),
getFloorEntry方法返回小于或等于给定key的键值对(优先返回等于的),
getHigherEntry方法返回大于给定key的键值对,
getLowerEntry方法返回小于给定key的键值对,
后三个方法的实现方式和getCeilingEntry方法非常类似,只是方向相反(以及判断条件不同),不再赘述
final Entry< K , V > getCeilingEntry ( K key) {
//p 为当前遍历节点,首先从根节点开始遍历
Entry< K , V > p = root ;
while (p != null ) {
// 比较给定 key p key
int cmp = compare(key , p. key ) ;
// 如果给定 key p key 小:如果 p 有左子节点则将 p 指向 p 的左子节点并继续遍历,如果 p 没有左子节点则返回 p
if (cmp < 0 ) {
if (p. left != null )
p = p. left ;
else
return p ;
// 如果给定 key p key
} else if (cmp > 0 ) {
// 如果 p 有右子节点,则将 p 指向 p 的右子节点并继续遍历
if (p. right != null ) {
p = p. right ;
// 如果 p 没有右子节点,则判断 p 的类型。如果 p 是它父亲的左子节点,则直接返回 p 的父亲;如果 p 是它父亲的右子节点,则往上推,直到他的某一层的祖先是其父亲的左子节点,返回这个祖先的父节点
// 这里这样判断的原因是,如果 p 是左子节点,那么它的父节点一定大于给定的 key ,如果 p 是右子节点,那么他的父节点一定是小于给定的 key ,所以要一直往上推,直到他的某一个祖先是左节点,这时候这个祖先的父节点是大于给定的 key
} 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 ;
}
// 如果给定 key p key 相等,则返回 p
} else
return p ;
}
return null;
}

将指定的键值对放入TreeMap中
public V put ( K key , V value) {
// 首先需要遍历 TreeMap ,找到适当的插入位置,当前遍历节点 t 首先指向 root ,从根节点开始遍历
Entry< K , V > t = root ;
// 如果 map 是空的,则创建一个新节点,并将其设置为根节点,返回 null
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 ;
// split comparator and comparable paths
// 如果有比较器,则使用比较器来查找插入的合适位置
Comparator super K > cpr = comparator ;
if (cpr != null ) {
do {
parent = t ;
cmp = cpr.compare(key , t. key ) ;
// 如果待插入 key t key 小,则将 t 指向 t 的左节点
if (cmp < 0 )
t = t. left ;
// 如果待插入 key t key 大,则将 t 指向 t 的右节点
else if (cmp > 0 )
t = t. right ;
// 如果两个 key 相等,则直接修改 t value 为方法传入的 value ,并返回旧 value
else
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 )
t = t. left ;
else if (cmp > 0 )
t = t. right ;
else
return t.setValue(value) ;
} while (t != null ) ;
}
// 如果遍历结束仍无法在 map 中找到和待插入的 key 相同的 key ,则创建一个新的 Entry
Entry< K , V > e = new Entry<>(key , value , parent) ;
// 根据之前最后一次的比较结果,确定新的 Entry 节点应该插入的 parent 的左侧还是右侧
if (cmp < 0 )
parent. left = e ;
else
parent. right = e ;
// 插入后,红黑树可能不再满足红黑树的条件,需要调用 fixAfterInsertion 方法使其重新成为一颗红黑树
fixAfterInsertion(e) ;
// 更新 size modCount ,并返回 null
size ++ ;
modCount ++ ;
return null;
}

删除指定的key对于的键值对,首先调用getEntry方法找到要删除的键值对,然后调用deleteEntry方法将其删除
public V remove (Object key) {
Entry< K , V > p = getEntry(key) ;
if (p == null )
return null;

V oldValue = p. value ;
deleteEntry(p) ;
return oldValue ;
}

调用forEach的时候,实际也是从这个树的最小值开始,一直遍历到最大值
public void forEach (BiConsumer super K , ? super V > action) {
Objects. requireNonNull (action) ;
int expectedModCount = modCount ;
for (Entry< K , V > e = getFirstEntry() ; e != null; e = successor (e)) {
action.accept(e. key , e. value ) ;

if (expectedModCount != modCount ) {
throw new ConcurrentModificationException() ;
}
}
}

PrivateEntryIterator类是所有TreeMap的Iterator类的基类,实现了hasNext、nextEntry、prevEntry、remove等方法

NavigableSubMap作为一个比较重要的内部类,基本上所有子map的实现都是基于它
内部维护了一个TreeMap
final TreeMap< K , V > m ;
子map的最低键lo、子map的最高键hi、子map是否从m的最小值开始(fromStart)、子map是否一直到m的结尾才结束(toEnd)、是否包含最小键lo(loInclusive)、是否包含最大键hi(hiInclusive)
final K lo , hi ;
final boolean fromStart , toEnd ;
final boolean loInclusive , hiInclusive ;
NavigableSubMap内部的所有方法都是基于上述这些实现的。

Entry类为内部使用的红黑树节点,包含:
K key ;
V value ;
Entry< K , V > left ;
Entry< K , V > right ;
Entry< K , V > parent ;
boolean color = BLACK ;

buildFromSorted方法是所有根据排序集合(包括反序列化TreeMap)创建新TreeMap的底层方法,这个方法比较抽象,如果不好理解,可以自己举个例子,比如使用一个总共4个节点的TreeMap,跟踪一下代码
private final Entry< K , V > buildFromSorted ( int level , int lo , int hi ,
int redLevel ,
Iterator it ,
java.io.ObjectInputStream str ,
V defaultVal)
throws java.io.IOException , ClassNotFoundException {

// 这是一个递归的方法,总体思路是按照迭代器的顺序去构建整个 map ,迭代器已经排好序了,所以不需要关系排序,只需要按顺序将红黑树组成即可

// 如果最高索引小于最低索引则返回 null
if (hi < lo) return null;
// 计算中间索引,他是最低索引加最高的值除以 2
int mid = (lo + hi) >>> 1 ;

Entry< K , V > left = null;
// 如果最低索引小于中间索引,继续递归调用 buildFromSorted ,传入参数的 level+1 (代表红黑树的层数), mid 自减 1 (这样才可能最终退出递归)
if (lo < mid)
left = buildFromSorted(level+ 1 , lo , mid - 1 , redLevel ,
it , str , defaultVal) ;
// 最终取得了左叶节点后,根据迭代器或输入流来创建中间节点
// extract key and/or value from iterator or stream
K key ;
V value ;
if (it != null ) {
if (defaultVal== null ) {
Map.Entry , ?> entry = (Map.Entry , ?>)it.next() ;
key = ( K )entry.getKey() ;
value = ( V )entry.getValue() ;
} else {
key = ( K )it.next() ;
value = defaultVal ;
}
} else { // use stream
key = ( K ) str.readObject() ;
value = (defaultVal != null ? defaultVal : ( V ) str.readObject()) ;
}

Entry< K , V > middle = new Entry<>(key , value , null ) ;
// 如果当前层级应该是红色,则将刚创建的节点置为红色, redLevel 是在外层方法中计算并传入的
// color nodes in non-full bottommost level red
if (level == redLevel)
middle. color = RED ;
// 如果有创建过左子节点,更新节点关系
if (left != null ) {
middle. left = left ;
left. parent = middle ;
}
// 如果中间节点小于最高节点,则递归调用 buildFromSorted 方法创建右子节点,并更新关系
if (mid < hi) {
Entry< K , V > right = buildFromSorted(level+ 1 , mid+ 1 , hi , redLevel ,
it , str , defaultVal) ;
middle. right = right ;
right. parent = middle ;
}
// 返回本次调用创建的中间节点(如果是最外层的调用,这个就是 map 的根节点)
return middle ;
}

这个方法在删除节点是调用
private void deleteEntry (Entry< K , V > p) {
modCount ++ ;
size -- ;
// 如果左右子树都存在,则找到他的后继节点(即比它大的节点中最小的那个节点,一般是他的右子树的最左侧子树),将后继节点的值赋给 p ,将 p 指向后继节点(即调换他们的位置)
// 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. key = s. key ;
p. value = s. value ;
p = s ;
} // p has 2 children
// 经过上一步以后, p 目前一定是只有左节点或只有右节点或没有子节点,我们下一步需要将 p 去掉,将 p 的唯一子节点(如果有的话)放到 p 原来的位置上
// Start fixup at replacement node, if it exists.
Entry< K , V > replacement = (p. left != null ? p. left : p. right ) ;
// 如果 p 有子节点,则设其子节点为 replacement
if (replacement != null ) {
// Link replacement to parent
// replacement 的父节点指向 p 的父节点
replacement. parent = p. parent ;
// p 的父节点的子节点指向 replacement ,到此,即完成了 replacement p 的替换
if (p. parent == null )
root = replacement ;
else if (p == p. parent . left )
p. parent . left = replacement ;
else
p. parent . right = replacement ;
// 完全删除 p
// Null out links so they are OK to use by fixAfterDeletion.
p. left = p. right = p. parent = null;
// 如果删除的 p 是黑色,则需要调整红黑树
// Fix replacement
if (p. color == BLACK )
fixAfterDeletion(replacement) ;
// 如果 p 没有子节点并且 p 没有父节点,则说明 p 是根节点,简单重置根节点为 null 即可
} else if (p. parent == null ) { // return if we are the only node.
root = null;
// 如果 p 没有子节点且 p 不是根节点,则可以直接删除 , 即去掉相关的指针,并且按需调整红黑树
} 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;
}
}
}

rotateLeft、rotateRight、fixAfterInsertion、fixAfterDeletion四个方法和hashmap中的方法基本一样,那几个方法在hashmap中已经看过,不再赘述

Spliterator相关的以后统一看

你可能感兴趣的:(java源码学习:1.8)