【java基础】TreeMap源码分析

TreeMap 简介

TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。
TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。
TreeMap 实现了Cloneable接口,意味着它能被克隆
TreeMap 实现了java.io.Serializable接口,意味着它支持序列化

TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
TreeMap的基本操作 containsKey、get、put 和 remove 的时间复杂度是 log(n) 。
另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。

TreeMap的底层储存

 TreeMap底层使用红黑树实现的,通过传入一个比较器来进行比较排序,比较器是实现Comparator接口的类:

public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

或者通过键的自然顺序进行比较排序,自然顺序指的是实现Comparable接口:

public interface Comparable<T> {
    public int compareTo(T o);
}

 TreeMap底层使用Entry<K,V>键值对来构造红黑树,通过对KEY进行比较构造。

    private final Comparator<? super K> comparator;//比较器

    private transient Entry<K,V> root = null;//根节点

    private transient int size = 0;//存储的数量
  • TreeMap的本质是R-B Tree(红黑树),它包含几个重要的成员变量: root, size, comparator。
  • root 是红黑数的根节点。它是Entry类型,Entry是红黑数的节点,它包含了红黑数的6个基本组成成分:key(键)、value(值)、left(左孩子)、right(右孩子)、parent(父节点)、color(颜色)。Entry节点根据key进行排序,Entry节点包含的内容为value。 
  • 红黑数排序时,根据Entry中的key进行排序;Entry中的key比较大小是根据比较器comparator来进行判断的。如果没有指定comparator,就通过键key的自然顺序排序,这就要求key实现了Comparable接口,否则插入时报错ClassCastException
  • size是红黑数中节点的个数。
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;//节点颜色,红还是黑
        //方法省略。。。。。。
}

TreeMap构造函数

// 默认构造函数。使用该构造函数,TreeMap中的元素按照自然排序进行排列。
TreeMap()

// 创建的TreeMap包含Map
TreeMap(Map<? extends K, ? extends V> copyFrom)

// 指定Tree的比较器
TreeMap(Comparator<? super K> comparator)

// 创建的TreeSet包含copyFrom
TreeMap(SortedMap<K, ? extends V> copyFrom)

TreeMap的节点插入


public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // 用来判断TreeMap是否有Comparator,或者key是否实现 
                               //comparable接口,否则报ClassCastException
            root = new Entry<>(key, value, null);//构造根节点
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        Comparator<? super K> cpr = comparator;
        //如果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();
            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;
    }
final int compare(Object k1, Object k2) {
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            : comparator.compare((K)k1, (K)k2);
} 

 可以看出TreeMap的插入是正常的二叉排序树的插入,最关键的是插入之后进行节点的调整,来保存整棵树的平衡。

fixAfterInsertion()函数就不多介绍了,具体看红黑树的插入调整,左旋和右旋,以及涂色。

TreeMap删除节点

 public V remove(Object key) {
        Entry<K,V> p = getEntry(key);//先找到这个key对应的键值对节点
        if (p == null)
            return null;

        V oldValue = p.value;
        deleteEntry(p);//删除这个键值对节点。
        return oldValue;
 }

//根据key查找对应的键值对节点
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();
        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;
    }

//删除指定键值对节点。
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的后继节点s
            p.key = s.key;
            p.value = s.value;
            p = s;
        } // p has 2 children

        // replacement节点指向后继节点s的左孩子或右孩子
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);

        if (replacement != null) {
            // 将replacement节点链接到原来后继节点的父节点上
            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;

            // 如果后继节点的颜色是黑色,那么将其删除后会破坏红黑树的平衡,需要调整
            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节点查找

//根据键来获取值 ,时间复杂度是红黑树的时间复杂度O(logn)
public V get(Object key) {
        Entry<K,V> p = getEntry(key);//上面说过了
        return (p==null ? null : p.value);
    }
//获取最小的键值对
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;
    }

TreeMap的迭代查找 

说到迭代查找,那肯定就少不了迭代接口Iterable,这里顺便说一下foreach用法

(1)遍历数组

(2)foreach用于遍历任何实现Iterable接口对象

public interface Iterable<T> {
    Iterator<T> iterator();
}

而TreeMap重写了父类AbstractMap的entrySet()方法。

 public Set<Map.Entry<K,V>> entrySet() {
        EntrySet es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
    }

 而EntrySet从其父类间接继承了Iterable接口。

 class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator(getFirstEntry());//getFirstEntry获取第一个(有序的)
                                                      //键值对节点
        }
        //省略其他方法。。。
}
final class EntryIterator extends PrivateEntryIterator<Map.Entry<K,V>> {
        EntryIterator(Entry<K,V> first) {
            super(first);
        }
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
    }
 final Entry<K,V> nextEntry() {
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            next = successor(e);//successor前面说过,获取有序的下一个键值对节点。
            lastReturned = e;
            return e;
        }

测试代码如下:

public static void main(String[] args) {
		TreeMap<Integer,String> m=new TreeMap<Integer,String> ();
		m.put(5, "555");
		m.put(1, "111");
		m.put(3, "333");
		m.put(4, "444");
		m.keySet().iterator();
		Iterator<Map.Entry<Integer, String>> it= m.entrySet().iterator();
		while(it.hasNext()){
			it.next();
		}
		for(Map.Entry<Integer, String> entry:m.entrySet()){
			System.out.println(entry.getKey()+","+entry.getValue());
		}
	}

输出结果为:

1,111
3,333
4,444
5,555

猜你喜欢

转载自blog.csdn.net/fxkcsdn/article/details/81747424