Java集合框架之四--------TreeMap与TreeSet源码分析

1.1TreeMap源码分析

使用TreeMap存储进行排序,要么自然排序,使用一些实现comparable接口的类的compareTo方法,要么自己定义比较机制,一种是user类去实现comparable接口,并实现compareTo方法,另一种是mycomparator写一个类去实现comparator接口实现compare方法,将mycomparator作为参数传入到treemap构造方法

《Java集合框架之四--------TreeMap与TreeSet源码分析》

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类的构造方法

默认comparatornull

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);

}

获得sortedmapsize

如果size0mapsize不为0,且mapsortedmap类型

—2.1获得map的比较器,

——2.1.1如果比较器与当前的比较器一样,执行buildfromsorted,后面分析

——2.1.2如果不一样,就调用父类putAll方法,内部调用了Treemapput方法

如果的条件不满足,就直接条用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;

}

《Java集合框架之四--------TreeMap与TreeSet源码分析》高度为2的树的最后一个节点的索引为2,类推高度为3的最后一个节点为6,满足m = (m + 1) * 2

———————————————————–

分为三部分;

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;

middlekeyvalue设定值

如果迭代器为空

—-key从流中获取,如果defaultval为空,value就是流中的对象,如果defaultval不为空,value就是defaultval

如果迭代器不为空

—-如果defaultval为空,将迭代器的next转为mapentry,设置keyvalue

—-如果defaultval不为空,key为迭代器的next转为keyvaluedefaultval

        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> middlenew 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查找,如果keynull,抛出异常;如果比较器为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添加操作

比较方法:如果comparatornull,调用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源码分析

《Java集合框架之四--------TreeMap与TreeSet源码分析》

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对象,keyE类型,valueObject类型

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中,如果mTreeMap类型,且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) {

        }

    }

 

 

    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/huangwei18351/article/details/82111738
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞