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相关的以后统一看