从零开始的Java容器学习(八):TreeMap

247 阅读4分钟

前言

设想一个场景,我们需要对每一个键值对映射的Key进行排序,在遍历时需要按照顺序获得键值对,这个时候就需要用到TreeMap了。

TreeMap的简要介绍

TreeMap是支持排序的Map实现,它的底层是红黑树,非线程安全。TreeMap的key不能为null,如果没有实现比较器则将根据自然排序对Key进行排序。

TreeMap继承自AbstractMap,实现了Serializable、Cloneable、NavigableMap接口,NavigableMap继承自SortedMap接口。

从源码分析TreeMap

成员变量

private final Comparator<? super K> comparator;//内部维护了一个比较器用来维护Key的顺序,如果为空则使用自然排序
private transient Entry<K,V> root;//红黑树的根节点
private transient int size = 0;//当前容器内的键值对数量

构造方法

public TreeMap() {//空参构造函数,使用自然排序
        comparator = null;
    }
public TreeMap(Comparator<? super K> comparator) {//传进来一个比较器,就用这个了
        this.comparator = comparator;
    }
public TreeMap(Map<? extends K, ? extends V> m) {//传进来一个Map则使用自然排序,putAll全部放进去
    comparator = null;
    putAll(m);
}
public TreeMap(SortedMap<K, ? extends V> m) {//传进来一个排序的Map,使用buildFromSorted转成TreeMap
    comparator = m.comparator();
    try {
        buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
    } catch (java.io.IOException cannotHappen) {
    } catch (ClassNotFoundException cannotHappen) {
    }
}

添加

public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {//根节点为null
        compare(key, key); // type (and possibly null) check//检测Key是否为null

        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;//如果比较器为空,就使用自然排序,Key必须实现Comparable接口
        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;
}

删除

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

    V oldValue = p.value;
    deleteEntry(p);//调用deleteEntry删除红黑树的节点
    return oldValue;
}
//红黑树部分留着以后写了,这里注重的是TreeMap的了解实现和掌握用法
//所以代码就贴出来看看就行,后面会出系列关于算法的文章会专门写一篇红黑树的
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.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;
            }
        }
    }

查询

public V get(Object key) {
    Entry<K,V> p = getEntry(key);//用getEntry来找
    return (p==null ? null : p.value);
}
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;
}
final Entry<K,V> getFirstEntry() {//找到第一个也就是最小的那个
    Entry<K,V> p = root;
    if (p != null)
        while (p.left != null)
            p = p.left;
    return p;
}
//同理还有getLastEntry
final Entry<K,V> getCeilingEntry(K key) {//找到key对应的Entry,如果没有就找到比他大的最小的那个,如果还没有就返回null
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = compare(key, p.key);
        if (cmp < 0) {
            if (p.left != null)
                p = p.left;
            else
                return p;
        } else if (cmp > 0) {
            if (p.right != null) {
                p = p.right;
            } else {
                Entry<K,V> parent = p.parent;
                Entry<K,V> ch = p;
                while (parent != null && ch == parent.right) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        } else
            return p;
    }
    return null;
}
//同理还有getFloorEntry

遍历

class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    public Iterator<Map.Entry<K,V>> iterator() {
        return new EntryIterator(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();
    }
}
//后面就比较啰嗦了,感兴趣自己翻,就不贴出来了

总结

TreeMap源码部分设计到红黑树的知识太多,在这里就不细讲了,主要说下TreeMap的适用场景:比如需要实现基于排序的统计功能,需要实现增删改查都在一个可接受的时间开销内(O(logN)),或者需要遍历时保证按照Key的排列顺序等等(一般只有在需要对key进行排序时才使用到TreeMap)。

总结一下,TreeMap底层为红黑树,要求key不为null,key重复会覆盖value,value可以重复,如果key没有比较器将根据自然顺序进行排序,增删改查的时间开销为(O(logN))