前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TreeMap源码解析

TreeMap源码解析

作者头像
103style
发布2022-12-19 13:33:34
3480
发布2022-12-19 13:33:34
举报

转载请以链接形式标明出处: 本文出自:103style的博客

base on jdk_1.8.0_77

目录

  • 红黑树简介
  • TreeMap简介
  • TreeMap的成员变量介绍
  • TreeMap的构造函数
  • TreeMap相关的函数
  • 小结
  • 参考文章

红黑树简介

红黑树 就是一种平衡的二叉查找树,说他平衡的意思是他不会变成“瘸子”,左腿特别长或者右腿特别长。除了符合二叉查找树的特性之外,还具体下列的特性:

  • 节点是红色或者黑色
  • 根节点是黑色
  • 每个叶子的节点都是黑色的空节点(NULL)
  • 每个红色节点的两个子节点都是黑色的。
  • 从任意节点到其每个叶子的所有路径都包含相同的黑色节点。
红黑树示例
红黑树示例

TreeMap简介

代码语言:javascript
复制
public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable

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

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

排序默认是升序的,数字比较大小,字符串比较首字母,其他类型则需要自己实现Comparable接口,否则排序时会报错。

测试代码:

代码语言:javascript
复制
public static void main(String[] args) {
    List<Integer> s = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        int t = (int) (Math.random() * 1000);
        s.add(t);
    }
    
    for (int i = 0; i < s.size(); i++) {
        System.out.print(s.get(i) + "----");
    }
    System.out.println();

    TreeMap<Integer, Integer> treeMap = new TreeMap<>();
    for (int i = 0; i < s.size(); i++) {
        treeMap.put(s.get(i), s.get(i));
    }

    Iterator<Map.Entry<Integer, Integer>> iterator = treeMap.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry<Integer, Integer> next = iterator.next();
        System.out.println(next.getKey() + "--" + next.getValue());
    }
}

输出结果:

代码语言:javascript
复制
953----411----437----516----461----238----201----365----345----191----
191--191
201--201
238--238
345--345
365--365
411--411
437--437
461--461
516--516
953--953

数据类型修改为String之后输出结果为:

代码语言:javascript
复制
65----296----701----75----278----650----507----71----839----547----
278--278
296--296
507--507
547--547
65--65
650--650
701--701
71--71
75--75
839--839

TreeMap的成员变量介绍

代码语言:javascript
复制
private final Comparator<? super K> comparator;//key的比较器
private transient TreeMapEntry<K,V> root;//数的根节点
private transient int size = 0;//treemap节点的数量
private transient int modCount = 0;//树修改的次数
private transient EntrySet entrySet;//键值对集合
private transient KeySet<K> navigableKeySet;//键集合
private transient NavigableMap<K,V> descendingMap;//降序的NavigableMap

TreeMap的构造函数

TreeMap的构造函数主要是处理 key 的比较器。

代码语言:javascript
复制
public TreeMap() {
    comparator = null;
}
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}
public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}
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) {
    }
}

TreeMap相关的函数

代码语言:javascript
复制
public V get(Object key)
public Map.Entry<K,V> ceilingEntry(K key)
public Map.Entry<K,V> floorEntry(K key)
public Map.Entry<K,V> higherEntry(K key)
public Map.Entry<K,V> lowerEntry(K key)
public V put(K key, V value)
public V replace(K key, V value)
public V remove(Object key) 
public void clear()
public Map.Entry<K,V> firstEntry()
public Map.Entry<K,V> lastEntry()
private void rotateLeft(TreeMapEntry<K,V> p)
private void rotateRight(TreeMapEntry<K,V> p)
private void fixAfterInsertion(TreeMapEntry<K,V> x)
private void fixAfterDeletion(TreeMapEntry<K,V> x)

get(Object key) and getEntry(Object key)

从根节点开始,比较节点的目标的key的大小,

  • 目标key>节点key:查找节点的右子树
  • 目标key<节点key:查找节点的左子树
  • 目标key=节点key:返回节点

当在没有设置comparator的时候,get方法的key = null会抛出NullPointerException.

代码语言:javascript
复制
public V get(Object key) {
    TreeMapEntry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}
final TreeMapEntry<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;
    TreeMapEntry<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 TreeMapEntry<K,V> getEntryUsingComparator(Object key) {
    @SuppressWarnings("unchecked")
    K k = (K) key;
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
        TreeMapEntry<K,V> p = root;
        while (p != null) {
            int cmp = cpr.compare(k, p.key);
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
    }
    return null;
}

ceilingEntry(K key)

获取 大于等于 key的最小节点。当 key比树中最大的key大时返回null

代码语言:javascript
复制
public Map.Entry<K,V> ceilingEntry(K key) {
    return exportEntry(getCeilingEntry(key));
}
final TreeMapEntry<K,V> getCeilingEntry(K key) {
    TreeMapEntry<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 {
                TreeMapEntry<K,V> parent = p.parent;
                TreeMapEntry<K,V> ch = p;
                while (parent != null && ch == parent.right) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        } else
            return p;
    }
    return null;
}

floorEntry(K key)

获取 小于等于 key的最大节点。当 key比树中最小的key小时返回null

代码语言:javascript
复制
public K floorKey(K key) {
    return keyOrNull(getFloorEntry(key));
}
final TreeMapEntry<K,V> getFloorEntry(K key) {
    TreeMapEntry<K,V> p = root;
    while (p != null) {
        int cmp = compare(key, p.key);
        if (cmp > 0) {
            if (p.right != null)
                p = p.right;
            else
                return p;
        } else if (cmp < 0) {
            if (p.left != null) {
                p = p.left;
            } else {
                TreeMapEntry<K,V> parent = p.parent;
                TreeMapEntry<K,V> ch = p;
                while (parent != null && ch == parent.left) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        } else
            return p;

    }
    return null;
}

higherEntry(K key)

获取 大于 key的最小节点,不存在则返回 null

代码语言:javascript
复制
public K higherKey(K key) {
    return keyOrNull(getHigherEntry(key));
}
final TreeMapEntry<K,V> getHigherEntry(K key) {
    TreeMapEntry<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 (p.right != null) {
                p = p.right;
            } else {
                TreeMapEntry<K,V> parent = p.parent;
                TreeMapEntry<K,V> ch = p;
                while (parent != null && ch == parent.right) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        }
    }
    return null;
}

lowerEntry(K key)

获取 小于 key的最大节点,不存在则返回null.

代码语言:javascript
复制
public K lowerKey(K key) {
    return keyOrNull(getLowerEntry(key));
}
final TreeMapEntry<K,V> getLowerEntry(K key) {
    TreeMapEntry<K,V> p = root;
    while (p != null) {
        int cmp = compare(key, p.key);
        if (cmp > 0) {
            if (p.right != null)
                p = p.right;
            else
                return p;
        } else {
            if (p.left != null) {
                p = p.left;
            } else {
                TreeMapEntry<K,V> parent = p.parent;
                TreeMapEntry<K,V> ch = p;
                while (parent != null && ch == parent.left) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        }
    }
    return null;
}

put(K key, V value)

  • 如果树为null,则构建一个TreeMapEntry设置为当前的root.
  • 检查之前TreeMapcomparator是否为空,不为空则用comparator去对比key,否则用k.compareTo(t.key)比较,然后遍历当前树,找到对应的key则修改对应的value
  • 如果遍历树没找到,则通过new TreeMapEntry<>(key, value, parent); 添加到树上,然后执行fixAfterInsertion(e)保证root还是一颗 红黑树fixAfterInsertion(e)下面会介绍。

红黑树执行插入操作之后,要执行“插入修正操作”。 目的是:保红黑树在进行插入节点之后,仍然是一颗红黑树

代码语言:javascript
复制
public V put(K key, V value) {
    TreeMapEntry<K, V> t = root;
    if (t == null) {
        compare(key, key); // type (and possibly null) check

        root = new TreeMapEntry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    TreeMapEntry<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;
        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);
    }
    TreeMapEntry<K, V> e = new TreeMapEntry<>(key, value, parent);
    if (cmp < 0)
        parent.left = e;
    else
        parent.right = e;
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}

replace(K key, V value)

通过getEntry(key)获取对应的节点,如果节点不为空,则更新值。

代码语言:javascript
复制
public V replace(K key, V value) {
    TreeMapEntry<K,V> p = getEntry(key);
    if (p!=null) {
        V oldValue = p.value;
        p.value = value;
        return oldValue;
    }
    return null;
}

remove(Object key)

通过getEntry(key)获取对应的节点,如果节点不为空,通过deleteEntry删除节点,并通过fixAfterDeletion(TreeMapEntry<K,V> x)重排树使之还是一颗 红黑树fixAfterDeletion下面会介绍。

红黑树执行删除之后,要执行“删除修正操作”。 目的是保证:红黑树删除节点之后,仍然是一颗红黑树

  • p有左右子树的时候,successor(p),及返回右子树上的最左边的树节点,即大于pkey的最小值。所以 replacement即为右子树上的最左边的树节点的右子树。否则 replacement即为p的左右子树中的一个。
  • 然后根据 replacement 不为空是否是根节点为空且不是根节点 3种情况删除节点p.
代码语言:javascript
复制
public V remove(Object key) {
    TreeMapEntry<K,V> p = getEntry(key);
    if (p == null)
        return null;

    V oldValue = p.value;
    deleteEntry(p);
    return oldValue;
}
private void deleteEntry(TreeMapEntry<K, V> p) {
    modCount++;
    size--;
    if (p.left != null && p.right != null) {// p has 2 children
        TreeMapEntry<K, V> s = successor(p);
        p.key = s.key;
        p.value = s.value;
        p = s;
    }
    TreeMapEntry<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;

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

clear()

置空root,修改 size0.

代码语言:javascript
复制
public void clear() {
    modCount++;
    size = 0;
    root = null;
}

firstEntry()

返回最左端的节点的key-value构建的SimpleImmutableEntry

代码语言:javascript
复制
public Map.Entry<K, V> firstEntry() {
    return exportEntry(getFirstEntry());
}
final TreeMapEntry<K, V> getFirstEntry() {
    TreeMapEntry<K, V> p = root;
    if (p != null)
        while (p.left != null)
            p = p.left;
    return p;
}
static <K, V> Map.Entry<K, V> exportEntry(TreeMapEntry<K, V> e) {
    return (e == null) ? null :
            new AbstractMap.SimpleImmutableEntry<>(e);
}

lastEntry()

返回最右端的节点的key-value构建的SimpleImmutableEntry

代码语言:javascript
复制
public Map.Entry<K,V> lastEntry() {
    return exportEntry(getLastEntry());
}
final TreeMapEntry<K,V> getLastEntry() {
    TreeMapEntry<K,V> p = root;
    if (p != null)
        while (p.right != null)
            p = p.right;
    return p;
}
static <K, V> Map.Entry<K, V> exportEntry(TreeMapEntry<K, V> e) {
    return (e == null) ? null :
            new AbstractMap.SimpleImmutableEntry<>(e);
}

rotateLeft(TreeMapEntry<K,V> p)

红黑树 的左旋

红黑树的左旋
红黑树的左旋

p 相当与上图的X.

代码语言:javascript
复制
private void rotateLeft(TreeMapEntry<K,V> p) {
    if (p != null) {
        TreeMapEntry<K,V> r = p.right;
        p.right = r.left;
        if (r.left != null)
            r.left.parent = p;
        r.parent = p.parent;
        if (p.parent == null)
            root = r;
        else if (p.parent.left == p)
            p.parent.left = r;
        else
            p.parent.right = r;
        r.left = p;
        p.parent = r;
    }
}

rotateRight(TreeMapEntry<K,V> p)

红黑树 的右旋

红黑树的右旋
红黑树的右旋

p 相当与上图的X.

代码语言:javascript
复制
private void rotateRight(TreeMapEntry<K,V> p) {
    if (p != null) {
        TreeMapEntry<K,V> l = p.left;
        p.left = l.right;
        if (l.right != null) l.right.parent = p;
        l.parent = p.parent;
        if (p.parent == null)
            root = l;
        else if (p.parent.right == p)
            p.parent.right = l;
        else p.parent.left = l;
        l.right = p;
        p.parent = l;
    }
}

fixAfterInsertion(TreeMapEntry<K,V> x)

  • 插入的节点,默认设置为红树。当x.parent.color == RED时,则需要进行旋转知道满足 红黑树 的条件。旋转的过程可以参考链接中的示例。
代码语言:javascript
复制
private void fixAfterInsertion(TreeMapEntry<K, V> x) {
    x.color = RED;

    while (x != null && x != root && x.parent.color == RED) {
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
            TreeMapEntry<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 {
            TreeMapEntry<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;
}

fixAfterDeletion(TreeMapEntry<K,V> x)

  • 删除的节点只有是 黑树 时,才需要进行旋转重新满足 红黑树 的条件。旋转的过程可以参考链接中的示例。
代码语言:javascript
复制
private void fixAfterDeletion(TreeMapEntry<K, V> x) {
    while (x != root && colorOf(x) == BLACK) {
        if (x == leftOf(parentOf(x))) {
            TreeMapEntry<K, V> sib = rightOf(parentOf(x));
            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);
                setColor(parentOf(x), RED);
                rotateLeft(parentOf(x));
                sib = rightOf(parentOf(x));
            }
            if (colorOf(leftOf(sib)) == BLACK &&
                    colorOf(rightOf(sib)) == BLACK) {
                setColor(sib, RED);
                x = parentOf(x);
            } else {
                if (colorOf(rightOf(sib)) == BLACK) {
                    setColor(leftOf(sib), BLACK);
                    setColor(sib, RED);
                    rotateRight(sib);
                    sib = rightOf(parentOf(x));
                }
                setColor(sib, colorOf(parentOf(x)));
                setColor(parentOf(x), BLACK);
                setColor(rightOf(sib), BLACK);
                rotateLeft(parentOf(x));
                x = root;
            }
        } else { // symmetric
            TreeMapEntry<K, V> sib = leftOf(parentOf(x));
            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);
                setColor(parentOf(x), RED);
                rotateRight(parentOf(x));
                sib = leftOf(parentOf(x));
            }
            if (colorOf(rightOf(sib)) == BLACK &&
                    colorOf(leftOf(sib)) == BLACK) {
                setColor(sib, RED);
                x = parentOf(x);
            } else {
                if (colorOf(leftOf(sib)) == BLACK) {
                    setColor(rightOf(sib), BLACK);
                    setColor(sib, RED);
                    rotateLeft(sib);
                    sib = leftOf(parentOf(x));
                }
                setColor(sib, colorOf(parentOf(x)));
                setColor(parentOf(x), BLACK);
                setColor(leftOf(sib), BLACK);
                rotateRight(parentOf(x));
                x = root;
            }
        }
    }

    setColor(x, BLACK);
}

小结

  • TreeMap是有序的key-value集合。
  • HashMap不保证数据有序,LinkedHashMap保证数据可以保持插入顺序,而TreeMap可以保持key的大小顺序的时候。

参考文章


以上

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-07-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 红黑树简介
  • TreeMap简介
  • TreeMap的成员变量介绍
  • TreeMap的构造函数
  • TreeMap相关的函数
    • get(Object key) and getEntry(Object key)
      • ceilingEntry(K key)
        • floorEntry(K key)
          • higherEntry(K key)
            • lowerEntry(K key)
            • put(K key, V value)
            • replace(K key, V value)
            • remove(Object key)
            • clear()
            • firstEntry()
            • lastEntry()
            • rotateLeft(TreeMapEntry<K,V> p)
            • rotateRight(TreeMapEntry<K,V> p)
            • fixAfterInsertion(TreeMapEntry<K,V> x)
            • fixAfterDeletion(TreeMapEntry<K,V> x)
            • 小结
            • 参考文章
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档