TreeSet对于实现了Comparable接口的对象或者comparator中的对象可以自动排序。
最基本的构造函数:所有面向外部的构造函数都会调用这个构造函数,初始化NavigableMap
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
利用对象Comparable接口进行排序的构造函数:利用TreeMap中的keys保存对象
利用compartaor进行排序的构造函数:利用TreeMap中的keys保存对象public TreeSet() { this(new TreeMap<E,Object>()); }
iterator:返回TreeMap中的iterator,其中next方法返回当前节点的后继节点public TreeSet(Comparator<? super E> comparator) { this(new TreeMap<>(comparator)); }
add:调用TreeMap中的put方法,也是红黑树的添加节点方法public Iterator<E> iterator() { return m.navigableKeySet().iterator(); }
remove:调用TreeMap中的remove方法,也是红黑树中的删除节点方法public boolean add(E e) { return m.put(e, PRESENT)==null; }
TreeSet利用TreeMap中的keys保存对象,TreeMap通过红黑树加入或删除这些对象,维持一个可排序的集合。public boolean remove(Object o) { return m.remove(o)==PRESENT; }
TreeMap分析
TreeMap本质上是一颗红黑树,put和remove方法对应了红黑树的插入和删除。下面分别分析TreeMap的put和remove方法
put:
BST的插入过程:
1从根节点开始搜索
2如果插入节点大于当前点则和当前节点的右子节点比较,反之和当前节点的左子节点比较
3反复执行2只到不能继续,此时和当前节点比较,如果大于当前节点则插入当前节点右子节点的位置,反之插入左边
remove: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; }
BST的删除过程:
1删除节点是叶子节点,直接删除
2删除节点只有左子树或者右子树,将左子树或者右子树直接连接到父节点对应的左子节点或者右子节点
3删除节点既有左子树也有右子树,找到该节点的后继节点,用后继节点替换该节点,并删除后继节点。
因为后继节点要么是叶子节点要么只有右子树,因此删除的过程和1,2是一样的。我们可以先执行3,在统一执行1,2
private void deleteEntry(Entry<K,V> p) { modCount++; size--; // 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 如果有后继节点,用后继节点代替删除节点,并把指针指向后继节点,后面和没有后节点的情况统一执行1,2步 // Start fixup at replacement node, if it exists. Entry<K,V> replacement = (p.left != null ? p.left : p.right); if (replacement != null) {//步骤2 // Link replacement to parent replacement.parent = p.parent; if (p.parent == null) root = replacement; else if (p == p.parent.left) p.parent.left = replacement; else p.parent.right = replacement; // Null out links so they are OK to use by fixAfterDeletion. p.left = p.right = p.parent = null; // Fix replacement if (p.color == BLACK) fixAfterDeletion(replacement);//修复红黑树 } else if (p.parent == null) { // return if we are the only node. root = null; } 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; } } }