Java集合类-TreeMap分析

TreeMap的特点

TreeMap基于红黑树实现

红黑树是一种弱平衡的二叉树,比AVL树旋转次数少,用弱平衡换取旋转次数

有5个性质:

性质1. 节点是红色或黑色。

性质2. 根节点是黑色。

性质3 每个叶节点(NIL节点,空节点)是黑色的。

性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

特性:最长路径不会长于最短路径2倍

成员变量

private final Comparator<? super K> comparator; // 结点的比较器,用于对树的结点进行排序

树的结点

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

 

构造函数

共有4个构造函数

1、无参构造函数,不指定比较器

public TreeMap() {
    comparator = null;
}

2、无参构造函数,指定比较器

public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}

3、参数是Map的构造函数

public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}

下面详细分析下putAll函数

public void putAll(Map<? extends K, ? extends V> map) {
    int mapSize = map.size();

    // 如果TreeMap的size为0,且放入的map的size不是0,且是已排序的Map
    if (size==0 && mapSize!=0 && map instanceof SortedMap) {
        Comparator<?> c = ((SortedMap<?,?>)map).comparator();
        
        // 放入map的比较器和TreeMap的比较器相同,使用buildFromSorted构建
        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;
        }
    }
    
    // 遍历插入map中的结点到TreeMap
    super.putAll(map);
}

1、如果TreeMap中没有结点,且插入的map是有已排序的map,并且map的比较器和TreeMap的比较器相同,则使用buildFromSorted进行插入

2、否则遍历map中的结点,然后插入到TreeMap

4、参数是SortedMap的构造函数

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) {
    }
}

插入的已排序的map,直接使用buildFromSorted进行插入 

下面分析下buildFromSorted是如何插入map到TreeMap的

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 {
        // lo 树的第一结点的索引 hi 树的最后一个结点的索引


        /*
         * Strategy: The root is the middlemost element. To get to it, we
         * have to first recursively construct the entire left subtree,
         * so as to grab all of its elements. We can then proceed with right
         * subtree.
         *
         * The lo and hi arguments are the minimum and maximum
         * indices to pull out of the iterator or stream for current subtree.
         * They are not actually indexed, we just proceed sequentially,
         * ensuring that items are extracted in corresponding order.
         */

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

        // extract key and/or value from iterator or stream
        // 获取k-v的值
        K key;
        V value;
        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());
        }

        // SortedMap中第一个结点作为TreeMap的根结点,默认是黑结点
        Entry<K,V> middle =  new Entry<>(key, value, null);

        // color nodes in non-full bottommost level red
        if (level == redLevel)
            middle.color = RED;

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

1、构建左子树,然后新建结点,然后构建右子树

2、最终形成的树是在叶子结点以上是一个满二叉树,所以满足红黑树的性质,叶子结点不满足,所以把叶子结点都染成红色

 

基本方法

增/改 put

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;

    // 找到插入结点在红黑树中的位置,如果key相等更新改结点value
    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;
}

 

找到插入结点的位置,然后新建结点插入,然后通过fixAfterInsertion调整红黑树的平衡

删 remove

public V remove(Object key) {
    Entry<K,V> p = getEntry(key);
    if (p == null)
        return null;

    V oldValue = p.value;
    deleteEntry(p);
    return oldValue;
}

 

先通过getEntry找到树结点,然后通过deleteEntry删除该结点

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 

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}

 

通过getEntry找到该结点 

一些问题

参考

http://blog.csdn.net/ns_code/article/details/36421085

http://blog.csdn.net/jiang_bing/article/details/7537803

 

posted @ 2017-10-25 22:11  【Java后端笔记】  阅读(195)  评论(0编辑  收藏  举报