Java集合框架之四--------TreeMap与TreeSet源码分析
1.1TreeMap源码分析
使用TreeMap存储进行排序,要么自然排序,使用一些实现comparable接口的类的compareTo方法,要么自己定义比较机制,一种是user类去实现comparable接口,并实现compareTo方法,另一种是mycomparator写一个类去实现comparator接口实现compare方法,将mycomparator作为参数传入到treemap构造方法
3.1.1类名及其成员变量
// 比较器对象 private final Comparator<? super K> comparator;
// 根节点 private transient Entry<K,V> root;
// 集合大小 private transient int size = 0;
// 树结构被修改的次数 private transient int modCount = 0;
// 静态内部类用来表示节点类型 static final class Entry<K,V> implements Map.Entry<K,V> { K key; // 键 V value; // 值 Entry<K,V> left; // 指向左子树的引用(指针) Entry<K,V> right; // 指向右子树的引用(指针) Entry<K,V> parent; // 指向父节点的引用(指针) boolean color = BLACK; // } |
3.1.2类的构造方法
默认comparator为null public TreeMap() { // 1,无参构造方法 comparator = null; // 默认比较机制 }
2,自定义比较器的构造方法,实现comparator接口,并作为参数传入 public TreeMap(Comparator<? super K> comparator) { // this.comparator = comparator; }
3,构造已知Map对象为TreeMap,比较器仍然为空 public TreeMap(Map<? extends K, ? extends V> m) { // comparator = null; // 默认比较机制 putAll(m); } 4,构造已知的SortedMap对象为TreeMap public TreeMap(SortedMap<K, ? extends V> m) { // comparator = m.comparator(); // 使用已知对象的构造器 try { 此方法为排序集合中构造TreeMap,复杂度为线性 buildFromSorted(m.size(), m.entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } } 这里插入讲putAll方法 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); } ①获得sortedmap的size ②如果size为0,map的size不为0,且map是sortedmap类型 ---2.1获得map的比较器, ------2.1.1如果比较器与当前的比较器一样,执行buildfromsorted,后面分析 ------2.1.2如果不一样,就调用父类putAll方法,内部调用了Treemap的put方法 ③如果②的条件不满足,就直接条用putall方法 ---------------------------------- 底层用TreeMap自身的put方法,后面介绍 public void putAll(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } -------------------------------------- private void buildFromSorted(int size, Iterator<?> it, java.io.ObjectInputStream str, V defaultVal) throws java.io.IOException, ClassNotFoundException { this.size = size; root = buildFromSorted(0, 0, size-1, computeRedLevel(size), it, str, defaultVal); } ------------------------------------------------------------- 计算完全二叉树的层数 private static int computeRedLevel(int sz) { int level = 0; for (int m = sz - 1; m >= 0; m = m / 2 - 1) level++; return level; }
----------------------------------------------------------- 分为三部分; 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 {
if (hi < lo) return null;
int mid = (lo + hi) >>> 1;
Entry<K,V> left = null; 递归构造左节点 if (lo < mid) left = buildFromSorted(level+1, lo, mid - 1, redLevel, it, str, defaultVal);
构造middle节点; K key; V value; 为middle的key和value设定值 如果迭代器为空 ----key从流中获取,如果defaultval为空,value就是流中的对象,如果defaultval不为空,value就是defaultval; 如果迭代器不为空 ----如果defaultval为空,将迭代器的next转为mapentry,设置key和value ----如果defaultval不为空,key为迭代器的next转为key,value为defaultval 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()); } 将key和value存到middle节点中 Entry<K,V> middle = new Entry<>(key, value, null); 只要把前面3层的结点都设置为黑色,第四层的节点设置为红色,则构造完的树,就是红黑树。level从0开始的,所以上述9个节点,计算出来的是3,实际上就是代表的第4层 if (level == redLevel) middle.color = RED; 如果左节点不为空,就将左节点作为middle的左孩子 if (left != null) { middle.left = left; left.parent = middle; } 构造右节点 if (mid < hi) { Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel, it, str, defaultVal); middle.right = right; right.parent = middle; }
return middle; } 大体思路: ①如果hi < lo,return null,表示为空,如果hi>=lo表示当前子树构造完成 ②获取mid 的值,lo + hi的一半 ③定义left entry为null;如果lo < mid; 构造左子树(left = 递归,其中level+1, hi = mid - 1) ④处理当前根节点的key和value;如果迭代器不为空,取map的key,value要么等于map的value,要么等于defaultval;如果迭代器为空,从str流中取key,value要么等于流的对象,要么等于defaultval; ⑤创建middle当前根节点,设定key,value;如果level==redlevel,标红,表示叶子节点即最后一层; ⑥如果left不为空,设定middle的left等于left,left的parent等于middle ⑦如果mid < hi;构建右子树(right = 递归,level+ 1,lo =mid +1);结束后将right作为mid的右孩子,mid作为right的父亲; ⑧返回middle |
3.1.3查找方法
底层调用getEntry,返回value值 public V get(Object key) { Entry<K,V> p = getEntry(key); return (p==null ? null : p.value); } 如果比较器不为null,调用getEntryUsingComparator查找,如果key为null,抛出异常;如果比较器为null,调用key实现的comparable接口的compareTo方法与红黑树的节点进行比较;小于0往左,大于0往右; 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; } 用比较器进行比较,如果比较器不为空,调用compare方法将key和红黑树的节点比较,同样小于0往左;大于0往右 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; } 获得红黑树的根节点,一直往左,就能得到最小的 final Entry<K,V> getFirstEntry() { Entry<K,V> p = root; if (p != null) while (p.left != null) p = p.left; return p; }
获得红黑树的根节点,一直往右,就能得到最大的 final Entry<K,V> getLastEntry() { Entry<K,V> p = root; if (p != null) while (p.right != null) p = p.right; return p; } |
3.1.4添加操作
比较方法:如果comparator为null,调用key的接口或者对象中带的compareto方法,否则就是用比较器的compare final int compare(Object k1, Object k2) { return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2) : comparator.compare((K)k1, (K)k2); } public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { 再次判断key不能为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; } ①得到根节点,如果根节点为空,检查key不能为空,然后将key,value构造一个entry,赋值为根节点 ②定义父亲节点,即插入节点的父亲节点,也就是插入的位置 ③寻找带插入的位置,也就是找parent;如果比较器不为空,用比较器的compare方法,如果比较器为空,使用key实现的接口或者自带的compareTo方法,小于0往左找,大于0往右找;如果等于0,调用setValue,将value值替换,返回新的value;并将当前节点赋值给parent ④根据比较的结果大于0还是小于0,将待插入节点设置为parent的左孩子或右孩子 ⑤调整红黑树 |
3.1.5删除操作
获取key对应的entry,如果是null,返回null,否则获取旧值,调用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; } 寻找t的后继节点;右子树不为空,就右子树最左,,否则向上找到第一个左父节点 目的在于找到最接近t的节点进行替换 static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) { if (t == null) return null; else if (t.right != null) { Entry<K,V> p = t.right; while (p.left != null) p = p.left; return p; } 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; } }
private void deleteEntry(Entry<K,V> p) { modCount++; size--; 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
// Start fixup at replacement node, if it exists. Entry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) { // 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; } } } 大体思路: ①如果有两个孩子,找到后继节点s,将p的key,value用s替代;p指向s,接下来对通过p间接操作s,则s变为只有左孩子或右孩子之一,或者没有孩子 ②如果p只有一个孩子,将p的孩子赋值给replacement,如果rep不为空,就将rep的parent指向p的parent;如果p的parent为空,说明p为根节点,root指向rep;如果p是左孩子,就p的parent的left指向rep,否则right指向rep,然后将p的左右孩子和父亲设为null,如果p是黑色,要进行调整 如果rep为空,且p的parent也为空,那么说明只有一个p节点,则root =null ③如果rep为空,p的parent不为空,说明p没有孩子;如果p是黑色,调整红黑树,如果p是左孩子,p的parent的left为空,否则right为空;然后p的parent为null |
3.2TreeSet源码分析
TreeSet是基于TreeMap实现的一个保证元素有序的集合,其底层的存储盒TreeMap一样也是一棵红黑树,执行和TreeMap的增删改查操作
3.2.1内部属性
定义navigablemap接口类型m private transient NavigableMap<E,Object> m; object类型的present,作为key-value中的value值,将需要保存的值放入key中 private static final Object PRESENT = new Object(); |
3.2.2构造方法
多态,其实m是TreeMap的实现类 TreeSet(NavigableMap<E,Object> m) { this.m = m; } 无参构造器,传入一个TreeMap对象,key为E类型,value为Object类型 public TreeSet() { this(new TreeMap<E,Object>()); } 传入一个带比较器的TreeMap public TreeSet(Comparator<? super E> comparator) { this(new TreeMap<>(comparator)); } 将指定集合放入treeset中 public TreeSet(Collection<? extends E> c) { this(); addAll(c); } 将有序集合s方法TreeSet中 public TreeSet(SortedSet<E> s) { this(s.comparator()); addAll(s); } 所有的构造方法都是调用TreeSet(NavigableMap<E,Object> m)因此m一定会被初始化为TreeMap类型
将集合加入TreeSet中,如果m是TreeMap类型,且c是有序的,使用map的方法进行添加,value就是Present;,如果不是有序的,就使用c集合实现类的addall方法 public boolean addAll(Collection<? extends E> c) { // Use linear-time version if applicable if (m.size()==0 && c.size() > 0 && c instanceof SortedSet && m instanceof TreeMap) { SortedSet<? extends E> set = (SortedSet<? extends E>) c; TreeMap<E,Object> map = (TreeMap<E, Object>) m; Comparator<?> cc = set.comparator(); Comparator<? super E> mc = map.comparator(); if (cc==mc || (cc != null && cc.equals(mc))) { map.addAllForTreeSet(set, PRESENT); return true; } } return super.addAll(c); } 默认调用有序的建立treemap的方法; void addAllForTreeSet(SortedSet<? extends K> set, V defaultVal) { try { buildFromSorted(set.size(), set.iterator(), null, defaultVal); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } } |