赞
踩
java中的TreeMap,TreeSet都是基于红黑树进行实现。
红黑树有如下两个特征:
红-黑规则
。红-黑规则:
红黑树实现
的。// 默认构造函数,TreeMap中的,节点顺序依赖于对key的自然排序
TreeMap()
// 指定Tree的比较器
TreeMap(Comparator<? super K> comparator)
public class TreeMap<K,V> extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
针对搜索目标返回最近匹配项的导航方法
,例如返回小于(大于)某个key的节点集合
,返回具有最大(最小)key的节点
;如果不存在这样的键,则返回nullObject.clone()方法
,合法的对该类实例进行字段复制。Obejct.clone()方法
,会抛出CloneNotSupportException
异常。TreeMap支持序列化
,能够通过序列化传输//自定义比较器,通过comparator接口我们可以对TreeMap的内部排序进行精密的控制 private final Comparator<? super K> comparator; // Entry节点,这个表示红黑树的根节点 private transient Entry<K,V> root; // TreeMap中元素的个数 private transient int size = 0; // TreeMap修改次数 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;//节点的颜色默认是黑色,black为true ... }
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); // 如果key < 根节点,则在左子树查找插入位置 if (cmp < 0) t = t.left; // 如果key > 根节点,则在右子树查找插入位置 else if (cmp > 0) t = t.right; // 否则,重置根节点的值 else return t.setValue(value); } while (t != null); } // 如果没有指定比较器,则使用key进行自然排序 else { if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") // 将key强制转换为Comparable<? super K>对象 Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); // 如果key < 根节点,则在左子树查找插入位置 if (cmp < 0) t = t.left; // 如果key > 根节点,则在右子树查找插入位置 else if (cmp > 0) t = t.right; // 否则,重置根节点的值 else return t.setValue(value); } while (t != null); } Entry<K,V> e = new Entry<>(key, value, parent); // 如果key < 红黑树中最小节点parent,则新节点成为parent的左节点 if (cmp < 0) parent.left = e; // 如果key > 红黑树中最小节点parent,则新节点成为parent的右节点 else parent.right = e; // 节点插入完后,需要调整红黑树的高度和颜色 fixAfterInsertion(e); size++; modCount++; return null; }
size
、modCount
的变化,return null
)插入元素
时,实际上是会去遍历左子树或者右子树
。遍历左子树还是右子树,需要根据具体的比较原则决定。如果制定了比较器,则根据比较器进行比较,否则按key的自然排序进行比较。其中,如果cmp < 0
, 则遍历其左子树,cmp > 0
遍历其右子树,cmp =0
则更改value;否则直到检索出合适的叶子节点为止。(size
、modCount
的变化,return null
)public V get(Object key) { //获取元素,若为空则返回null否则返回其值 Entry<K,V> p = getEntry(key); return (p==null ? null : p.value); } 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; }
get(Object key)方法
调用了getEntry(Object key)方法
,判断getEntry(Object key)方法的返回值是否为null
。如果为null,则表明没有与key对应的节点;get(Object key)方法也返回null。否则,get(Object key)方法返回p.value。getEntryUsingComparator
,若没有则进行第二步NullPointerException
,从这点可以看出TreeMap是不允许Key-value为空
的public V remove(Object key) { // 获取key对应的节点 Entry<K,V> p = getEntry(key); // 节点不存在 if (p == null) return null; V oldValue = p.value; // 删除节点 deleteEntry(p); return oldValue; } private void deleteEntry(Entry<K,V> p) { modCount++; //修改次数 +1 size--; //元素个数 -1 /* * 被删除节点的左子树和右子树都不为空,那么就用 p节点的中序后继节点代替 p 节点 * successor(P)方法为寻找P的替代节点。规则是右分支最左边,或者 左分支最右边的节点 * ---------------------(1) */ if (p.left != null && p.right != null) { Entry<K,V> s = successor(p); p.key = s.key; p.value = s.value; p = s; } //replacement为替代节点,如果P的左子树存在那么就用左子树替代,否则用右子树替代 Entry<K,V> replacement = (p.left != null ? p.left : p.right); /* * 删除节点,分为上面提到的三种情况 * -----------------------(2) */ //如果替代节点不为空 if (replacement != null) { replacement.parent = p.parent; /* *replacement来替代P节点 */ //若P没有父节点,则跟节点直接变成replacement if (p.parent == null) root = replacement; //如果P为左节点,则用replacement来替代为左节点 else if (p == p.parent.left) p.parent.left = replacement; //如果P为右节点,则用replacement来替代为右节点 else p.parent.right = replacement; //同时将P节点从这棵树中剔除掉 p.left = p.right = p.parent = null; /* * 若P为红色直接删除,红黑树保持平衡 * 但是若P为黑色,则需要调整红黑树使其保持平衡 */ if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parent == null) { //p没有父节点,表示为P根节点,直接删除即可 root = null; } else { //P节点不存在子节点,直接删除即可 if (p.color == BLACK) //如果P节点的颜色为黑色,对红黑树进行调整 fixAfterDeletion(p); //删除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; } } }
remove(Object key)方法
通过getEntry(Object key)方法获取对应节点
,若节点为null
,则返回null;节点非null
,则调用deleteEntry(Entry<K,V>)方法删除对应的节点,并返回oldValue。首先更新modCount和size的值
,然后根据分情况讨论,删除对应节点
。删除节点后,调用fixAfterDeletion(p)方法,进行红黑树的结构调整
。参考链接:
JAVA学习-TreeMap详解
Java提高篇(二七)-----TreeMap
Java集合之TreeMap详解
// 默认构造函数。使用该构造函数,TreeSet中的元素按照自然排序进行排列。
TreeSet()
// 指定TreeSet的比较器
TreeSet(Comparator<? super E> comparator)
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
AbstractSet
,所以它是一个Set集合,具有Set的属性和方法。NavigableSet接口
,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。Cloneable接口
,意味着它能被克隆。java.io.Serializable接口
,意味着它支持序列化。sortedSet的唯一实现类
SortedSet subSet(Object fromElement,Object toElement) :返回这个Set的子集合,范围从fromElement(包含)到toElement(不包含)
SortedSet headSet(Object toElement):返回这个Set的子集合,范围小于到toElement的子集合
SortedSet tailSet(Object fromElement):返回这个Set的子集合,范围大于或等于到fromElement的子集合
不是线程安全的
。public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
// 用于保存TreeMap的对象,会在构造函数当中赋值TreeMap对象
private transient NavigableMap<E,Object> m;
// TreeMap当中所有的value都是保存的PRESENT对象
private static final Object PRESENT = new Object();
}
//添加元素,调用m.put方法实现
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
//查找是否包含元素,调用m.containsKey(o)方法实现
public boolean contains(Object o) {
return m.containsKey(o);
}
//删除方法,调用m.remove()方法实现
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
for(Iterator iter = set.iterator(); iter.hasNext(); ) {
iter.next();
}
1. 为什么使用红黑树,而不是普通的二叉查找树
2. 为什么数据库却使用B+树?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。