JDK1.8 Collection知识点与代码分析--TreeMap

本期系列文章的其他文章

JDK1.8 Collection知识点与代码分析–整体框架
JDK1.8 Collection知识点与代码分析–ArrayList&LinkedList
JDK1.8 Collection知识点与代码分析–HashMap
JDK1.8 Collection知识点与代码分析–LinkedHashMap
JDK1.8 Collection知识点与代码分析–HashSet&TreeSet

这篇文章接着打开TreeMap的源码进行分析. 首先TreeMap是util工具类中一个非常好用的有序映射, 自身是非线程安全的, 相比HashMap, 增加了对NavigableMap, sortedMap接口的实现, 增加了一系列基于键有序的api, 这里先列举几个比较常用的
sortedMap

  • firstKey(): 返回最小的key
  • lastKey(): 返回最大的key
  • subMap(from, to): 返回的是map的view, 左闭右开
  • headMap(toKey, inclusive): 返回比toKey严格小(inclusive为true时包括)的这部分map的view
  • tailMap(fromKey, inclusive): 返回比fromKey严格大(inclusive为true时包括)的这部分map的view

NavigableMap(继承SortedMap)

  • ceilingEntry(key)/ ceilingKey(key): 返回大于等于key参数的最小的元素, 如果没有这样的元素存在返回null
  • floorEntry(key)/floorKey(key): 返回小于等于key参数的最大元素, 如果没有这样的元素, 返回null
  • higherEntry/higherKey: 和ceiling类似, 只不过严格大
  • lowerEntry/lowerKey: 和floor类似, 只不过严格小

TreeMap的核心数据结构是红黑树, 其红黑树的实现和HashMap中的红黑树实现几乎是一模一样的, 只不过函数名和获取父节点, 左右节点的方法有微小的差别, 以下贴出核心部分的代码.
fixAfterInsertion这个方法和HashMap中的balanceInsertion是一模一样的, 下面的注释看完没有弄懂的可以看下这篇讲hashMap的文章中的balanceInsertion部分, 我在注释中通过图解的方式, 对每个部分解决的问题进行了梳理.

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

    while (x != null && x != root && x.parent.color == RED) {
        // x不是root节点, x的父节点为红节点
        // x的父节点是祖先节点的左儿子
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
            Entry<K,V> y = rightOf(parentOf(parentOf(x)));
            if (colorOf(y) == RED) {
                // flip Color
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                setColor(parentOf(parentOf(x)), RED);
                x = parentOf(parentOf(x));
            } else {

                if (x == rightOf(parentOf(x))) {
                    // x是右节点, x和父节点是红节点, 所以先左旋
                    x = parentOf(x);
                    rotateLeft(x);
                }
                // 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) {
                //flip
                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;
}

TreeMap中一个需要注意的细节, 当它没有指定comparator时, 是不允许null作为key的, 使用comparator时, 取决于comparator是否允许key为null.

对于NavigatableMap的api中的方法, 这里只给出一个例子, 它们的思路都非常相似

final Entry<K,V> getHigherEntry(K key) {
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = compare(key, p.key);
        if (cmp < 0) {
            // 如果当前节点比key大, 就继续往左找, 看是否能找到比当前更小的比key大的元素
            if (p.left != null)
                p = p.left;
            else
                return p;
        } else {
            if (p.right != null) {
                p = p.right;
            } else {
                // 如果更大的没有了, 往父节点找, 找到第一个比key大的父节点(回溯,找到第一个向左走的位置)
                Entry<K,V> parent = p.parent;
                Entry<K,V> ch = p;
                while (parent != null && ch == parent.right) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        }
    }
    return null;
}

这个方法的返回值并不是直接返回的, 而是通过exportEntry方法, 构造了一个SimpleImmutableEntry的实例进行发布, 保证安全性.

而对于SortedMap的api中方法, 它们返回的都是TreeMap的静态内部类AscendingSubMap, 这个静态内部类又是继承于TreeMap的抽象静态内部类NavigableSubMap, 这个类的成员变量如下:


/**
         * The backing map.
         */
        final TreeMap<K,V> m;

        /**
         * Endpoints are represented as triples (fromStart, lo,
         * loInclusive) and (toEnd, hi, hiInclusive). If fromStart is
         * true, then the low (absolute) bound is the start of the
         * backing map, and the other values are ignored. Otherwise,
         * if loInclusive is true, lo is the inclusive bound, else lo
         * is the exclusive bound. Similarly for the upper bound.
         */
        final K lo, hi;
        final boolean fromStart, toEnd;
        final boolean loInclusive, hiInclusive;

可以看到, 它是成员TreeMap的一个view, 内部维护了lo,hi两个key, 它的ceiling, floor等方法, 都是在检查是否在lo, hi范围后, 调用TreeMapgetCeilingEntry等方法.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章