说明
基于红黑树的 NavigableMap 实现。Map根据其键的 Comparable natural ordering 进行排序,或者根据Map创建时提供的 Comparator 进行排序,具体取决于使用的构造函数。
此实现为 containsKey、 get、 put 和 remove操作提供了保证的 log(n) 时间成本。算法改编自 Cormen、Leiserson 和 Rivest 的算法简介。
请注意,树形映射维护的排序,就像任何排序映射一样,无论是否提供显式比较器,必须与 equals 一致,如果此排序映射要正确实现 Map}接口。 之所以如此,是因为 Map接口是根据 equals 操作,但排序映射使用其 compareTo(或 compare)方法执行所有键比较,因此从排序映射的角度来看,此方法认为相等的两个键是相等的。有序映射的行为 是明确定义的,即使它的排序与 equals 不一致;它只是不遵守 Map 接口的一般约定。
注意这个实现不是同步的。如果多个线程同时访问一个映射,并且至少有一个线程在结构上修改了映射,它必须在外部同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改与现有键关联的值不是结构修改。)这通常是通过同步一些自然封装映射的对象来完成的。如果不存在此类对象,则应使用 CollectionssynchronizedSortedMap Collections.synchronizedSortedMap 方法“包装”Map。这最好在创建时完成,以防止对地图的意外不同步访问: SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));
返回的迭代器iterator 类的所有“集合视图方法”返回的集合的方法都是fail-fast:如果在迭代器创建后的任何时间对Map进行结构修改,除了通过迭代器的自己的 remove 方法,迭代器将抛出一个 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒着任意、非确定性行为的风险。
请注意,无法保证迭代器的快速失败行为,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬性保证。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误。
所有 Map.Entry此类中的方法及其视图返回的对表示映射生成时的快照。他们不支持 Entry.setValue 方法。 (但请注意,可以使用 put 更改关联映射中的映射。)
源码说明(部分)
/**
* 用于维护此树图中顺序的比较器,如果使用其键的自然顺序,则为 null。
*/
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;
/**
* tree中的键值对的数量
*/
private transient int size = 0;
/**
* 对树的结构修改次数。
*/
private transient int modCount = 0;
/**
* 使用其键的自然顺序构造一个新的空树映射。
*/
public TreeMap() {
comparator = null;
}
/**
* 使用给定的键的顺序构造一个新的空树映射
*/
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
/**
* 构造一个包含与给定映射相同的映射的新树映射,根据其键的自然排序排序。
*/
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
/**
* 构造一个包含相同映射并使用与指定排序映射相同的排序的新树映射。此方法在线性时间内运行。
*/
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) {
}
}
/**
*返回键值映射的数量
*/
public int size() {
return size;
}
/**
*map中是否存在相应的键值,存在返回true
*/
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
/**
* map中是否存在一个或多个相应的value值。
*/
public boolean containsValue(Object value) {
for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
if (valEquals(value, e.value))
return true;
return false;
}
/**
* 获得相应键值对应的value,如果存在该key,则返回value,否则null
*/
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
public Comparator<? super K> comparator() {
return comparator;
}
/**
* 获得第一个key
*/
public K firstKey() {
return key(getFirstEntry());
}
/**
* 获得最后一个key
*/
public K lastKey() {
return key(getLastEntry());
}
/**
* 复制给定的map到当前map中
*/
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);
}
/**
*获得给定key的Entry
*/
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) 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;
}
/**
* 使用比较器的 getEntry 版本。从 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;
}
/**
* 获取指定键对应的条目;如果不存在这样的条目,则返回大于指定键的最小键的条目;如果不存在这样的条目(即树中最大的键小于指定的键),则返回 null。
*/
final Entry<K,V> getCeilingEntry(K key) {
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 (cmp > 0) {
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;
}
} else
return p;
}
return null;
}
/**
* 获取指定键对应的条目;如果不存在这样的条目,则返回小于指定键的最大键的条目;如果不存在这样的条目,则返回 null。
*/
final Entry<K,V> getFloorEntry(K key) {
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.parent;
}
return parent;
}
} else
return p;
}
return null;
}
/**
* 获取大于指定键的最小键的条目;如果不存在这样的条目,则返回大于指定键的最小键的条目;如果不存在这样的条目,则返回 null。
*/
final Entry<K,V> getHigherEntry(K key) {
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;
}
/**
*返回小于指定键的最大键的条目;如果不存在这样的条目(即树中的最小键大于指定的键),则返回 null。
*/
final Entry<K,V> getLowerEntry(K key) {
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 (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.parent;
}
return parent;
}
}
}
return null;
}
/**
* A将指定值与此映射中的指定键相关联。如果映射先前包含键的映射,则旧值将被替换。
*/
public V put(K key, V value) {
Entry<K,V> t = root;
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);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
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);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
/**
* 如果存在该键值,则返回键所对应的值,并且移除
*/
public V remove(Object key) {
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
V oldValue = p.value;
deleteEntry(p);
return oldValue;
}
/**
* 清空该map
*/
public void clear() {
modCount++;
size = 0;
root = null;
}
总结
简单概述
- TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;
- TreeMap继承了NavigableMap接口,NavigableMap接口继承了
- SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现;
- TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;
- TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序;
对于红黑树的了解可以查看红黑树
红黑树
红黑树规则特点:
- 节点分为红色或者黑色;
- 根节点必为黑色;
- 叶子节点都为黑色,且为null;
- 连接红色节点的两个子节点都为黑色(红黑树不会出现相邻的红色节点);
- 从任意节点出发,到其每个叶子节点的路径中包含相同数量的黑色节点;
- 新加入到红黑树的节点为红色节点;
红黑树自平衡基本操作:
- 变色:在不违反上述红黑树规则特点情况下,将红黑树某个node节点颜色由红变黑,或者由黑变红;
- 左旋:逆时针旋转两个节点,让一个节点被其右子节点取代,而该节点成为右子节点的左子节点
- 右旋:顺时针旋转两个节点,让一个节点被其左子节点取代,而该节点成为左子节点的右子节点
Entry的实现
Entry静态内部类实现了Map的内部接口Entry,提供了红黑树存储结构的java实现,通过left属性可以建立左子树,通过right属性可以建立右子树,通过parent可以往上找到父节点。
构造函数可以见上述源码分析。
put方法流程
代码见源码分析。
其他操作可以通过源码查看。
具体内容详见TreeMap