【Java】TreeMap 解析

379 阅读3分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

一、前言

红黑树是于 1972 年发明的, 当时称为对称 二叉 B 树, 1978 年得到优化, 正式命名为红黑树。

它的主要特征是在每个节点上增加一个属性来表示节点的颜色, 可以是红色, 也可以是黑色。

红黑树在本质上还是二叉查找树,它额外引入 5个约束条件:

  1. 节点只能是红色或黑色
  2. 根节点必须是黑色
  3. 所有 NIL 节点都是黑色的。(NIL,即叶子节点下挂的两个虚节点,树尾端)
  4. 一条路径上不能出现相邻的两个红色节点
  5. 在任何递归子树内,根节点到叶子节点的所有路径上包含相同数目的黑色节点

Tips:有红必有黑,红红不相连 若一个树的左节点或右节点不存在,则均认定为黑色

上述5个约束条件保证了以下操作最坏时间复杂度为 O(log(n))

  • 新增
  • 删除
  • 查找

1. 红黑树 与 AVL 树比较

处于业务场景比较:

  • 如果查找次数主导树的更新次数:AVL 树更适合。

AVL 树比红黑树保持更加严格的平衡规则,在 AVL 树中查找通常更快,但这是以更多旋转操作导致更慢的插入和删除为代价的。

  • 如果更新次数主导树的查找次数:红黑树更适合。

HashMap 最初是数组和链表,只有在频繁的插入导致冲突之后才会升级为红黑树,因此可以大概率判断HashMap 如果发生了升级则添加和删除应该是比较频繁的,因此红黑树更合适一些。

对红黑树与 AVL 树同时进行以下操作:按顺序依次插入 36、 34、 37、 33、 35、32: 2021-08-3120-31-35.png

左侧 AVL 树的绝对平衡, 以及右侧红黑树的相对平衡。


2. TreeMap

  • TreeMap 底层是基于红黑树做的数据结构。

  • TreeMap 是按照 Key 的排序结果来组织内部结构的 Map 类集合, 它改变了 Map 类散乱无序的形象。 2021-08-3120-35-53.png

TreeMap 的接口继承树申, 有两个与众不同的接口 SortedMapNavigableMap

  1. SortedMap 接口:表示它的 Key 是有序不可重复的, 支持获取头尾 Key-Value 元素, 或者根据 Key 指定范围获取子集合等。

插入的 Key 必须实现 Comparable 或提供额外的比较器 Comparator , 所以 Key 不允许为 null , 但是 Value 可以;

  1. NavigableMap 接口:继承了 SortedMap 接口 , 根据指定的搜索条件返回最匹配的 Key-Value 元素 。

    不同于 HashMap, TreeMap 并非定要覆写 hashCodeequals 方法来达到 Key 去重的目的。



二、自定义排序规则

TreeMap 默认是按照 Key 大小来排序:

先来一个 demo ,看看 TreeMap 排序效果:

public class Test {
    public static void main(String[] args) {
        Map<Integer, String> map = new TreeMap<>();
        map.put(2, "牛yyds");
        map.put(1, "哈xswl");
        map.put(10, "牛哇牛哇");
        map.put(4, "牛yyds-1");
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println(entry);
        }
    }
}

输出结果:

1=哈xswl
2=牛yyds
4=牛yyds-1
10=牛哇牛哇

当然可以指定排序规则,倒排 - 从大到小:

public class Test {
    public static void main(String[] args) {
        Map<Integer, String> map = new TreeMap<>((o1, o2) -> o2 - o1);
        map.put(2, "牛yyds");
        map.put(1, "哈xswl");
        map.put(10, "牛哇牛哇");
        map.put(4, "牛yyds-1");
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println(entry);
        }
    }
}

输出结果如下:

10=牛哇牛哇
4=牛yyds-1
2=牛yyds
1=哈xswl