【JAVA】TreeMap 详解

TreeMap

TreeMap 是 Java 中一种实现了 NavigableMap 接口的有序 Map,它基于红黑树(Red-Black Tree)实现。与 HashMap 不同,TreeMap 中的键是有序的,这意味着它能够以排序的方式存储键值对,并提供与排序相关的操作。以下是 TreeMap 的详细介绍:

1. 基本概念

  • 有序性:TreeMap 中的元素是按照键的自然顺序(即实现 Comparable 接口)或通过提供的 Comparator 进行排序的。无论是插入、删除还是遍历操作,TreeMap 都会维护键的顺序。
  • 红黑树:TreeMap 底层基于红黑树实现,红黑树是一种自平衡二叉搜索树。在红黑树中,插入和删除操作的时间复杂度为 O(log n),而查找操作也能在 O(log n) 时间内完成。

2. 主要特性

  • 自动排序:TreeMap 自动根据键的顺序对元素进行排序。如果使用自定义的 Comparator,则会按照自定义规则进行排序。
  • 线程不安全:与 HashMap 类似,TreeMap 不是线程安全的。如果需要在多线程环境中使用,可以使用 Collections.synchronizedSortedMap 方法获得一个同步的 TreeMap,或者使用 ConcurrentSkipListMap。
  • 不允许空键:TreeMap 不允许存储 null 键,否则会抛出 NullPointerException。但允许值为 null。

3. 核心类与字段

3.1 Entry<K, V> 类

TreeMap 中的每个元素被封装在一个 Entry<K, V> 对象中。Entry 类定义如下:

static final class Entry<K,V> implements Map.Entry<K,V> {
    K key;           // 键
    V value;         // 值
    Entry<K,V> left; // 左子节点
    Entry<K,V> right;// 右子节点
    Entry<K,V> parent; // 父节点
    boolean color = BLACK; // 节点颜色(红色或黑色)

    Entry(K key, V value, Entry<K,V> parent) {
        this.key = key;
        this.value = value;
        this.parent = parent;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

    public V setValue(V value) {
        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }
}

3.2 重要字段

  • Entry<K, V> root:红黑树的根节点。
  • Comparator<? super K> comparator:用于排序的比较器。如果为 null,则使用键的自然顺序(Comparable)。
  • int size:TreeMap 中键值对的数量。
  • int modCount:结构修改次数的计数器,用于快速失败机制。

4. 核心方法

4.1 put(K key, V value) 方法

put 方法用于将一个键值对插入 TreeMap,并维护红黑树的平衡。

public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {
        compare(key, key); // type (and possibly null) check

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

流程概述:

  1. 查找插入位置:从根节点开始,根据键的大小找到插入的位置。比较逻辑通过 Comparator 或键的自然顺序来实现。
  2. 插入新节点:在找到正确的位置后,将新节点插入到红黑树中,并维护其父子关系。
  3. 维护红黑树平衡:调用 fixAfterInsertion 方法调整树的结构,确保红黑树的平衡性。

4.2 get(Object key) 方法

get 方法用于根据键查找对应的值。

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    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;
}

流程概述:

  1. 查找节点:从根节点开始,通过比较键的大小查找对应的节点。
  2. 返回结果:如果找到节点,返回其值;如果未找到,返回 null。

5. 其他常用方法

  • firstKey() / lastKey():返回 TreeMap 中最小/最大的键。
  • headMap(K toKey) / tailMap(K fromKey):返回比指定键小/大的子映射(视图)。
  • subMap(K fromKey, K toKey):返回一个键在指定范围内的子映射(视图)。
  • remove(Object key):根据键删除对应的键值对,并维护红黑树的平衡。

6. 使用场景

  • 需要排序的场景:如果需要按键的自然顺序或自定义顺序进行存储和访问,TreeMap 是首选。例如,需要按时间排序的日志记录或按字母顺序排序的词典。
  • 范围查找:TreeMap 提供了一些视图方法,如 subMap、headMap、tailMap,允许高效地查找某个范围内的键值对。
  • 自动平衡:由于 TreeMap 基于红黑树实现,插入、删除操作后自动维护平衡,适用于需要频繁插入、删除且需要保持有序的场景。

7. 性能

  • 时间复杂度:TreeMap 的查找、插入、删除操作的时间复杂度都是 O(log n),因为它基于红黑树这种平衡二叉搜索树。
  • 空间复杂度:由于需要维护指向父节点、子节点的引用,TreeMap 在空间上比 HashMap 略显复杂。

通过 TreeMap,开发者可以方便地管理和操作有序数据集合,在需要排序、范围查找等功能的场景中非常实用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值