java源码分析之TreeMap基础篇

本文深入探讨了基于红黑树实现的Java集合类TreeMap。TreeMap提供了有序的键值对,通过Comparable或Comparator进行排序。文章介绍了Comparable和Comparator接口,并详细分析了TreeMap的put、get、remove等核心操作的源码,包括节点插入后的调整和删除节点后的修复策略,展示了TreeMap如何维护红黑树的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常见的数据结构有数组、链表,还有一种结构也很常见,那就是树。前面介绍的集合类有基于数组的ArrayList,有基于链表的LinkedList,还有链表和数组结合的HashMap,今天介绍基于树的TreeMap。

     TreeMap基于红黑树(点击查看树、红黑树相关内容)实现。查看“键”或“键值对”时,它们会被排序(次序由Comparable或Comparator决定)。TreeMap的特点在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。 

     在介绍TreeMap前先介绍Comparable和Comparator接口。 

     Comparable接口:

1 public interface Comparable<T> {
2     public int compareTo(T o);
3 }

     Comparable接口支持泛型,只有一个方法,该方法返回负数、零、正数分别表示当前对象“小于”、“等于”、“大于”传入对象o。

     Comparamtor接口:

1 public interface Comparator<T> {
2 int compare(T o1, T o2);
3 boolean equals(Object obj);
4 }

     compare(T o1,T o2)方法比较o1和o2两个对象,o1“大于”o2,返回正数,相等返回零,“小于”返回负数。

     equals(Object obj)返回true的唯一情况是obj也是一个比较器(Comparator)并且比较结果和此比较器的结果的大小次序是一致的。即comp1.equals(comp2)意味着sgn(comp1.compare(o1, * o2))==sgn(comp2.compare(o1, o2))。

     补充:符号sgn(expression)表示数学上的signum函数,该函数根据expression的值是负数、零或正数,分别返回-1、0或1。

     小结一下,实现Comparable结构的类可以和其他对象进行比较,即实现Comparable可以进行比较的类。而实现Comparator接口的类是比较器,用于比较两个对象的大小。

     下面正式分析TreeMap的源码。

     既然TreeMap底层使用的是树结构,那么必然有表示节点的对象。下面先看TreeMap中表示节点的内部类Entry。

复制代码
 1 static final class Entry<K,V> implements Map.Entry<K,V> {
 2 // 键值对的“键”
 3 K key;
 4 // 键值对的“值”
 5     V value;
 6     // 左孩子
 7     Entry<K,V> left = null;
 8     // 右孩子
 9     Entry<K,V> right = null;
10     // 父节点
11     Entry<K,V> parent;
12     // 红黑树的节点表示颜色的属性
13     boolean color = BLACK;
14     /**
15      * 根据给定的键、值、父节点构造一个节点,颜色为默认的黑色
16      */
17     Entry(K key, V value, Entry<K,V> parent) {
18         this.key = key;
19         this.value = value;
20         this.parent = parent;
21     }
22     // 获取节点的key
23     public K getKey() {
24         return key;
25     }
26     // 获取节点的value
27     public V getValue() {
28         return value;
29     }
30     /**
31      * 修改并返回当前节点的value
32      */
33     public V setValue(V value) {
34         V oldValue = this.value;
35         this.value = value;
36         return oldValue;
37     }
38     // 判断节点相等的方法(两个节点为同一类型且key值和value值都相等时两个节点相等)
39     public boolean equals(Object o) {
40         if (!(o instanceof Map.Entry))
41             return false;
42         Map.Entry<?,?> e = (Map.Entry<?,?>)o;
43         return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
44     }
45     // 节点的哈希值计算方法
46     public int hashCode() {
47         int keyHash = (key==null ? 0 : key.hashCode());
48         int valueHash = (value==null ? 0 : value.hashCode());
49         return keyHash ^ valueHash;
50     }
51     public String toString() {
52         return key + "=" + value;
53     }
54 }
复制代码

    上面的Entry类比较简单,实现了树节点的必要内容,提供了hashCode方法等。下面看TreeMap类的定义。

1 public class TreeMap<K,V>
2     extends AbstractMap<K,V>
3     implements NavigableMap<K,V>, Cloneable, java.io.Serializable

     上面只有一个接口需要说明,那就是NavigableMap接口。

     NavigableMap接口扩展的SortedMap,具有了针对给定搜索目标返回最接近匹配项的导航方法。方法lowerEntry、floorEntry、ceilingEntry和higherEntry分别返回与小于、小于等于、大于等于、大于给定键的键关联的Map.Entry对象,如果不存在这样的键,则返回null。类似地,方法lowerKey、floorKey、ceilingKey和higherKey只返回关联的键。所有这些方法是为查找条目而不是遍历条目而设计的(后面会逐个介绍这些方法)。

     下面是TreeMap的属性:

复制代码
1      // 用于保持顺序的比较器,如果为空的话使用自然顺保持Key的顺序
2     private final Comparator<? super K> comparator;
3     // 根节点
4     private transient Entry<K,V> root = null;
5     // 树中的节点数量
6     private transient int size = 0;
7     // 多次在集合类中提到了,用于举了结构行的改变次数
8     private transient int modCount = 0;
复制代码

    注释中已经给出了属性的解释,下面看TreeMap的构造方法。

复制代码
 1 // 构造方法一,默认的构造方法,comparator为空,即采用自然顺序维持TreeMap中节点的顺序
 2 public TreeMap() {
 3     comparator = null;
 4 }
 5 // 构造方法二,提供指定的比较器
 6 public TreeMap(Comparator<? super K> comparator) {
 7     this.comparator = comparator;
 8 }
 9 // 构造方法三,采用自然序维持TreeMap中节点的顺序,同时将传入的Map中的内容添加到TreeMap中
10 public TreeMap(Map<? extends K, ? extends V> m) {
11     comparator = null;
12     putAll(m);
13 }
14 /** 
15 *构造方法四,接收SortedMap参数,根据SortedMap的比较器维持TreeMap中的节点顺序,* 同时通过buildFromSorted(int size, Iterator it, java.io.ObjectInputStream str, V defaultVal)方* 法将SortedMap中的内容添加到TreeMap中
16 */
17 public TreeMap(SortedMap<K, ? extends V> m) {
18     comparator = m.comparator();
19     try {
20         buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
21     } catch (java.io.IOException cannotHappen) {
22     } catch (ClassNotFoundException cannotHappen) {
23     }
24 }
复制代码

     TreeMap提供了四个构造方法,已经在注释中给出说明。构造方法中涉及到的方法在下文中会有介绍。

     下面从put/get方法开始,逐个分析TreeMap的方法。

     put(K key, V value)

复制代码
 1     public V put(K key, V value) {
 2         Entry<K,V> t = root;
 3         if (t == null) {
 4         //如果根节点为null,将传入的键值对构造成根节点(根节点没有父节点,所以传入的父节点为null)
 5             root = new Entry<K,V>(key, value, null);
 6             size = 1;
 7             modCount++;
 8             return null;
 9         }
10         // 记录比较结果
11         int cmp;
12         Entry<K,V> parent;
13         // 分割比较器和可比较接口的处理
14         Comparator<? super K> cpr = comparator;
15         // 有比较器的处理
16         if (cpr != null) {
17             // do while实现在root为根节点移动寻找传入键值对需要插入的位置
18             do {
19                 // 记录将要被掺入新的键值对将要节点(即新节点的父节点)
20                 parent = t;
21                 // 使用比较器比较父节点和插入键值对的key值的大小
22                 cmp = cpr.compare(key, t.key);
23                 // 插入的key较大
24                 if (cmp < 0)
25                     t = t.left;
26                 // 插入的key较小
27                 else if (cmp > 
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值