TreeMap实现原理

在用TreeMap以前咱们要对TreeMap有个总体的认识。java

一、TreeMap介绍

TreeMap是一个经过红黑树实现有序的key-value集合。node

TreeMap继承AbstractMap,也即实现了Map,它是一个Map集合算法

TreeMap实现了NavigableMap接口,它支持一系列的导航方法,缓存

TreeMap实现了Cloneable接口,它能够被克隆数据结构

TreeMap introduction:A Red-Black tree based NavigableMap implementation. The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
This implementation provides guaranteed log(n) time cost for the containsKey, get, put and remove operations. Algorithms are adaptations of those in Cormen, Leiserson, and Rivest’s Introduction to Algorithms.

TreeMap基于红黑树(Red-Black tree)实现。映射根据其键的天然顺序进行排序,或者根据建立映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。TreeMap的基本操做containsKey、get、put、remove方法,它的时间复杂度是log(n)。--(翻译JDK中关于TeeMap简介)app

TreeMap是非同步的。ide

TreeMap与Map的关系图以下:函数

TreeMap本质是Red-Black Tree,它包含几个重要的成员变量:root、size、comparator。其中root是红黑树的根节点。它是Entry类型,Entry是红黑树的节点,它包含了红黑树的6个基本组成:key、value、left、right、parent和color。Entry节点根据根据Key排序,包含的内容是value。Entry中key比较大小是根据比较器comparator来进行判断的。size是红黑树的节点个数。具体的红黑树算法,请自行百度。性能

二、TreeMap源码解析(JDK1.6版本)

为了更了解TreeMap的原理,下面咱们将根据TreeMap中实现的方法来对源码作一个详细的说明。ui

2.一、TreeMap数据结构

    TreeMap的定义以下:

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable

 TreeMap继承AbstractMap,实现NavigableMap、Cloneable、Serializable三个接口。其中AbstractMap代表TreeMap为一个Map即支持key-value的集合, NavigableMap则意味着它支持一系列的导航方法,具有针对给定搜索目标返回最接近匹配项的导航方法 。

    TreeMap中同时包含以下几个重要的属性:

/**
     * The comparator used to maintain order in this tree map, or
     * null if it uses the natural ordering of its keys.
     * 比较器,用来给TreeMap排序
     * @serial
     */
    private final Comparator<? super K> comparator;
	/**
	 * 红黑树的根节点
	 */
    private transient Entry<K,V> root = null;

    /**
     * The number of entries in the tree
	 * 红黑树的节点总数
     */
    private transient int size = 0;

    /**
     * The number of structural modifications to the tree.
	 * 红黑树的修改次数
     */
    private transient int modCount = 0;

    // Red-black mechanics
    /**
     * 红黑树的颜色--红色
     */
    private static final boolean RED   = false;

    /**
     * 红黑树的颜色--黑色
     */
    private static final boolean BLACK = true;

    对于叶子节点Entry来讲,Entry是TreeMap的内部类,它有几个重要属性:

static final class Entry<K,V> implements Map.Entry<K,V> {
	//键
	K key;
	//值
        V value;
	//左孩子
        Entry<K,V> left = null;
	//右孩子
        Entry<K,V> right = null;
	//父亲
        Entry<K,V> parent;
	//颜色
        boolean color = BLACK;

        /**
         * Make a new cell with given key, value, and parent, and with
         * <tt>null</tt> child links, and BLACK color.
         */
        Entry(K key, V value, Entry<K,V> parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

        /**
         * Returns the key.
         *
         * @return the key
         */
        public K getKey() {
            return key;
        }

        /**
         * Returns the value associated with the key.
         *
         * @return the value associated with the key
         */
        public V getValue() {
            return value;
        }

        /**
         * Replaces the value currently associated with the key with the given
         * value.
         *
         * @return the value associated with the key before this method was
         *         called
         */
        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;

            return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
        }

        public int hashCode() {
            int keyHash = (key==null ? 0 : key.hashCode());
            int valueHash = (value==null ? 0 : value.hashCode());
            return keyHash ^ valueHash;
        }

        public String toString() {
            return key + "=" + value;
        }
    }

2.二、TreeMap的构造器

    2.2.一、默认构造器

        使用默认构造器构造TreeMap时,使用java的默认的比较器比较Key的大小,从而对TreeMap进行排序

/**
     * Constructs a new, empty tree map, using the natural ordering of its keys. 
     * 默认构造器
     */
    public TreeMap() {
        comparator = null;
    }

    2.2.二、带比较器的构造函数

/**
     * Constructs a new, empty tree map, ordered according to the given comparator. 
     * 给定比较器的构造函数
     */
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

    2.2.三、带Map的构造函数,Map会成为TreeMap的子集

/**
     * Constructs a new tree map containing the same mappings as the given
     * map
     */
    public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null;
        putAll(m);
    }

    该构造函数会调用putAll()将m中的全部元素添加到TreeMap中。putAll()源码以下 :

/**
     * Copies all of the mappings from the specified map to this map.
     * These mappings replace any mappings that this map had for any
     * of the keys currently in the specified map.
	 * 将Map中的节点所有添加到TreeMap中
     */
    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);
    }

    2.2.四、带SortedMap的构造函数,SortedMap会成为TreeMap的子集

/**
     * Constructs a new tree map containing the same mappings and
     * using the same ordering as the specified sorted map.  This
     * method runs in linear time.
     */
    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
    // 将map中的元素逐个添加到TreeMap中,并返回map的中间元素做为根节点。
    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 {
        /*
         * 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) / 2;

        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 key;
        V value;
        if (it != null) {
            if (defaultVal==null) {
                Map.Entry<K,V> entry = (Map.Entry<K,V>)it.next();
                key = entry.getKey();
                value = entry.getValue();
            } else {
                key = (K)it.next();
                value = defaultVal;
            }
        } else { // use stream
            key = (K) str.readObject();
            value = (defaultVal != null ? defaultVal : (V) str.readObject());
        }

        Entry<K,V> middle =  new Entry<K,V>(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;
    }

对buildFromSorted方法解读   

  • buildFromSorted是经过递归将SortedMap中的元素逐个关联
  • buildFromSorted返回middle节点做为root
  • buildFromSorted添加到红黑树中时,只将level == redLevel的节点设为红色。

2.三、TreeMap实现的Serializable接口

    TreeMap实现了java.io.Serializable,分别实现了串行读取、写入功能。串行写入函数是writeObject(),它的做用是将TreeMap的容量,全部的Entry都写入到输出流。而串行读取函数是readObject(),它的做用是将TreeMap的容量,全部的Entry依次读出。经过readObject和writeObject可以帮助咱们实现TreeMap的串行传输。

private static final long serialVersionUID = 919286545866124006L;

    /**
     * Save the state of the <tt>TreeMap</tt> instance to a stream (i.e.,
     * serialize it).
     *
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out the Comparator and any hidden stuff
        s.defaultWriteObject();

        // Write out size (number of Mappings)
        s.writeInt(size);

        // Write out keys and values (alternating)
        for (Iterator<Map.Entry<K,V>> i = entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<K,V> e = i.next();
            s.writeObject(e.getKey());
            s.writeObject(e.getValue());
        }
    }

    /**
     * Reconstitute the <tt>TreeMap</tt> instance from a stream (i.e.,
     * deserialize it).
     */
    private void readObject(final java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in the Comparator and any hidden stuff
        s.defaultReadObject();

        // Read in size
        int size = s.readInt();

        buildFromSorted(size, null, s, null);
    }

2.四、TreeMap实现了Cloneable接口

    TreeMap实现了Cloneable接口,即实现了clone()方法。clone()方法的做用很简单,就是克隆一个TreeMap对象并返回。

/**
     * Returns a shallow copy of this <tt>TreeMap</tt> instance. (The keys and
     * values themselves are not cloned.)
     *
     * @return a shallow copy of this map
     */
    public Object clone() {
        TreeMap<K,V> clone = null;
        try {
            clone = (TreeMap<K,V>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }

        // Put clone into "virgin" state (except for comparator)
        clone.root = null;
        clone.size = 0;
        clone.modCount = 0;
        clone.entrySet = null;
        clone.navigableKeySet = null;
        clone.descendingMap = null;

        // Initialize clone with our mappings
        try {
            clone.buildFromSorted(size, entrySet().iterator(), null, null);
        } catch (java.io.IOException cannotHappen) {
        } catch (ClassNotFoundException cannotHappen) {
        }

        return clone;
    }

2.五、TreeMap重要方法的实现原理

    TreeMap继承AbstractMap,也便是一个Map。这里对map的主要方法作一个解析

    2.5.一、TreeMap的put()方法实现原理

/**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     */
    public V put(K key, V value) {
	//用t表示二叉树的当前节点
        Entry<K,V> t = root;
		//t为null表示一个空树,即TreeMap中没有任何元素,直接插入
        if (t == null) {
		//将新的key-value键值对建立为一个Entry节点,并将该节点赋予给root
            root = new Entry<K,V>(key, value, null);
            size = 1;
			//修改次数加1
            modCount++;
            return null;
        }
		//cmp表示key排序的返回结果
        int cmp;
        Entry<K,V> parent;
        // 指定的排序算法
        Comparator<? super K> cpr = comparator;
		//若是cpr不为空,则采用给定的排序算法建立TreeMap集合
        if (cpr != null) {
            do {
                parent = t;//parent指向上次循环后的t
                cmp = cpr.compare(key, t.key);
				//cmp返回值小于0,表示新增节点的key小于当前key的值,则以当前节点的左孩子做为新的当前节点
				//不然,以当前节点的右孩子做为新的当前节点
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
		//若是cpr为空,则采用默认的排序算法进行建立TreeMap集合
        else {
            if (key == null)
                throw new NullPointerException();
            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<K,V>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
		//上面已经完成了排序二叉树的构建,将新增节点插入该树中的合适位置,下面fixAfterInsertion方法就是对这棵树进行调整、平衡
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

    上述代码中do代码块实现排序二叉树的核心算法,经过该算法咱们能够确认新增节点在该树的正确位置。找到该位置后将插入便可,这样作其实尚未完成,由于咱们知道TreeMap的底层实现是红黑树,红黑树是一个平衡排序二叉树,普通的排序二叉树可能会出现失衡的状况,因此下一步就是要进行调整。fixAfterInsertion(e); 调整的过程务必会涉及到红黑树的左旋、右旋、着色三个基本操做。代码以下:

/** From CLR */
    private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED;

        while (x != null && x != root && x.parent.color == RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }

    这段代码中包含了左旋(rotateLeft())、右旋(rotateRight())和着色(setColor)等符合红黑树新增节点的处理过程。

    2.5.二、TreeMap的remove()方法实现原理

/**
     * Removes the mapping for this key from this TreeMap if present.
     *
     */
    public V remove(Object key) {
        Entry<K,V> p = getEntry(key);
        if (p == null)
            return null;

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

    经过这段代码能够看出,TreeMap的remove()方法中执行删除的真正方式是deleteEntry()方法。deleteEntry()代码以下:

/**
     * Delete node p, and then rebalance the tree.
     */
    private void deleteEntry(Entry<K,V> p) {
        modCount++;//修改次数 +1;
        size--;//元素个数 -1

        // If strictly internal, copy successor's element to p and then make p
        // point to successor.
		/*
         * 被删除节点的左子树和右子树都不为空,那么就用 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;
        } // p has 2 children

        // Start fixup at replacement node, if it exists.
		//replacement为替代节点,若是P的左子树存在那么就用左子树替代,不然用右子树替代
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);
		/*
         * 删除节点,分为上面提到的三种状况
         * -----------------------(2)
         */
        //若是替代节点不为空
        if (replacement != null) {
            // Link replacement to parent
			//replacement来替代P节点
            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;
            }
        }
    }

 三、TreeMap的使用场景

    咱们了解了什么是TreeMap以及它的部分实现原理。那咱们该如何运用treeMap呢?TreeMap是Map接口的具体实现,它的应用场景与Map应用场景大致相同。Map用于保存具备"映射关系"的数据,所以Map集合里保存着两组值,一组值用于保存Map里的key,另一组值用于保存Map里的value。key和value均可以是任何引用类型的数据。Map的key不容许重复,即同一个Map对象的任何两个key经过equals方法比较结果老是返回false。 关于Map,咱们要从代码复用的角度去理解,java是先实现了Map,而后经过包装了一个全部value都为null的Map就实现了Set集合 Map的这些实现类和子接口中key集的存储形式和Set集合彻底相同(即key不能重复) Map的这些实现类和子接口中value集的存储形式和List很是相似(即value能够重复、根据索引来查找) 。TreeMap一般比HashMap、Hashtable要慢(尤为是在插入、删除key-value对时更慢),由于TreeMap底层采用红黑树来管理键值对。可是TreeMap有一个好处就是:TreeMap中的key-value对老是处于有序状态,无须专门进行排序操做。而且虽然TreeMap在插入和删除方面性能比较差,可是在分类处理的时候做用很大,遍历的速度很快。TreeMap的使用示例

/**
	 * 该方法用于得到初始化成员变量sysbasecodeCache,调用remote接口,
	 * 得到全部的cbossBaseCode,并按code_type分
	 * 类,每一类编码存放在一个TreeMap中,并把全部生成的TreeMap做 为m_SysBaseTable的成员保存。
	 * 
	 * @return void
	 * @throws
	 */
	public static void initSysBaseCode() throws CBossDataAccessException {
		long start = System.currentTimeMillis();
		CBossLogUtil.getLogger(BaseCodeHelper.class).info(">>开始缓存["+CbossBaseCodeDataModule.TABLE_NAME+"]数据...");
		// 初始化前先清理缓存
		cleancache();
		int iCodeType = -999;
		Map<String, CbossBaseCodeDataModule> treeMap = null;
		Connection conn = null;
		try {
			conn = DataSourceProxy.getConnectionBySchema(CbossBaseCodeDataModule.SCHEMA_NAME);
			CbossBaseCodeDAO basedao = new CbossBaseCodeDAO(conn);
			List<CbossBaseCodeDataModule> baseCodes = basedao.selectAll();
			for (CbossBaseCodeDataModule cbossBaseCode : baseCodes) {
				iCodeType = cbossBaseCode.getCodeType();
				treeMap = (TreeMap) sysbasecodeCache.get(new Integer(iCodeType));
				if (treeMap == null) {
					treeMap = new TreeMap<String, CbossBaseCodeDataModule>();
					sysbasecodeCache.put(new Integer(iCodeType), treeMap);
				}
				treeMap.put(cbossBaseCode.getCodeId(), cbossBaseCode);
			}
		} catch (Exception e) {
			throw new CBossDataAccessException(e.getMessage() + "基础代码初始化时出现异常");
		} finally {
			DataSourceProxy.closeConnection(conn);
			CBossLogUtil.getLogger(BaseCodeHelper.class).info(">>["+CbossBaseCodeDataModule.TABLE_NAME+"]数据缓存完成, cost("+(System.currentTimeMillis() - start)+")ms");
		}
	}

    在遍历TreeMap的时候,可以直接肯定分类,而后在分类中遍历,最终可以以最快的速度获取想要的结果。

相关文章
相关标签/搜索