1、TreeMap介绍

TreeMap是一个通过红黑树实现有序的key-value集合。

TreeMap继承AbstractMap,也即实现了Map,它是一个Map集合

TreeMap实现了NavigableMap接口,它支持一系列的导航方法,

TreeMap实现了Cloneable接口,它可以被克隆

TreeMap introduction:A Red-Black tree based NavigableMap implementation. The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used. This implementation provides guaranteed log(n) time cost for the containsKey, get, put and remove operations. Algorithms are adaptations of those in Cormen, Leiserson, and Rivest’s Introduction to Algorithms.

TreeMap基于红黑树(Red-Black tree)实现。映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。TreeMap的基本操作containsKey、get、put、remove方法,它的时间复杂度是log(n)。--(翻译JDK中关于TeeMap简介)

TreeMap是非同步的。

TreeMap与Map的关系图如下:

TreeMap本质是Red-Black Tree,它包含几个重要的成员变量:root、size、comparator。其中root是红黑树的根节点。它是Entry类型,Entry是红黑树的节点,它包含了红黑树的6个基本组成:key、value、left、right、parent和color。Entry节点根据根据Key排序,包含的内容是value。Entry中key比较大小是根据比较器comparator来进行判断的。size是红黑树的节点个数。具体的红黑树算法,请自行百度。

2、TreeMap源码解析(JDK1.6版本)

为了更了解TreeMap的原理,下面我们将根据TreeMap中实现的方法来对源码做一个详细的说明。

2.1、TreeMap数据结构

TreeMap的定义如下:

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

 TreeMap继承AbstractMap,实现NavigableMap、Cloneable、Serializable三个接口。其中AbstractMap表明TreeMap为一个Map即支持key-value的集合, NavigableMap则意味着它支持一系列的导航方法,具备针对给定搜索目标返回最接近匹配项的导航方法 。

TreeMap中同时包含如下几个重要的属性:

/*** The comparator used to maintain order in this tree map, or* null if it uses the natural ordering of its keys.* 比较器,用来给TreeMap排序* @serial */ private final Comparator<? super K> comparator; /** * 红黑树的根节点 */ private transient Entry<K,V> root = null; /** * The number of entries in the tree * 红黑树的节点总数 */ private transient int size = 0; /** * The number of structural modifications to the tree. * 红黑树的修改次数 */ private transient int modCount = 0; // Red-black mechanics /** * 红黑树的颜色--红色 */ private static final boolean RED = false; /** * 红黑树的颜色--黑色 */ private static final boolean BLACK = true;

对于叶子节点Entry来说,Entry是TreeMap的内部类,它有几个重要属性:

static final class Entry<K,V> implements Map.Entry<K,V> { //键 K key; //值 V value; //左孩子 Entry<K,V> left = null; //右孩子 Entry<K,V> right = null; //父亲 Entry<K,V> parent; //颜色 boolean color = BLACK; /** * Make a new cell with given key, value, and parent, and with * <tt>null</tt> child links, and BLACK color. */ Entry(K key, V value, Entry<K,V> parent) { this.key = key; this.value = value; this.parent = parent; } /** * Returns the key. * * @return the key */ public K getKey() { return key; } /** * Returns the value associated with the key. * * @return the value associated with the key */ public V getValue() { return value; } /** * Replaces the value currently associated with the key with the given * value. * * @return the value associated with the key before this method was * called */ public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; return valEquals(key,e.getKey()) && valEquals(value,e.getValue()); } public int hashCode() { int keyHash = (key==null ? 0 : key.hashCode()); int valueHash = (value==null ? 0 : value.hashCode()); return keyHash ^ valueHash; } public String toString() { return key + "=" + value; } }

2.2、TreeMap的构造器

2.2.1、默认构造器

使用默认构造器构造TreeMap时,使用java的默认的比较器比较Key的大小,从而对TreeMap进行排序

/*** Constructs a new, empty tree map, using the natural ordering of its keys. * 默认构造器*/public TreeMap() { comparator = null; }

2.2.2、带比较器的构造函数

/*** Constructs a new, empty tree map, ordered according to the given comparator. * 给定比较器的构造函数*/public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; }

2.2.3、带Map的构造函数,Map会成为TreeMap的子集

/*** Constructs a new tree map containing the same mappings as the given* map*/public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); }

该构造函数会调用putAll()将m中的所有元素添加到TreeMap中。putAll()源码如下 :

/*** Copies all of the mappings from the specified map to this map.* These mappings replace any mappings that this map had for any* of the keys currently in the specified map.* 将Map中的节点全部添加到TreeMap中*/public void putAll(Map<? extends K, ? extends V> map) { int mapSize = map.size(); if (size==0 && mapSize!=0 && map instanceof SortedMap) { Comparator c = ((SortedMap)map).comparator(); if (c == comparator || (c != null && c.equals(comparator))) { ++modCount; try { buildFromSorted(mapSize, map.entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } return; } } super.putAll(map); }

2.2.4、带SortedMap的构造函数,SortedMap会成为TreeMap的子集

/*** Constructs a new tree map containing the same mappings and* using the same ordering as the specified sorted map.  This* method runs in linear time.*/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) { } }

带map参数的构造器都调用了buildFromSorted()。buildFromSorted()设计代码如下:

// 根据已经一个排好序的map创建一个TreeMap// 将map中的元素逐个添加到TreeMap中,并返回map的中间元素作为根节点。private final Entry<K,V> buildFromSorted(int level, int lo, int hi, int redLevel, Iterator it, java.io.ObjectInputStream str, V defaultVal) throws java.io.IOException, ClassNotFoundException { /* * Strategy: The root is the middlemost element. To get to it, we * have to first recursively construct the entire left subtree, * so as to grab all of its elements. We can then proceed with right * subtree. * * The lo and hi arguments are the minimum and maximum * indices to pull out of the iterator or stream for current subtree. * They are not actually indexed, we just proceed sequentially, * ensuring that items are extracted in corresponding order. */ if (hi < lo) return null; int mid = (lo + hi) / 2; Entry<K,V> left = null; if (lo < mid) left = buildFromSorted(level+1, lo, mid - 1, redLevel, it, str, defaultVal); // extract key and/or value from iterator or stream K key; V value; if (it != null) { if (defaultVal==null) { Map.Entry<K,V> entry = (Map.Entry<K,V>)it.next(); key = entry.getKey(); value = entry.getValue(); } else { key = (K)it.next(); value = defaultVal; } } else { // use stream key = (K) str.readObject(); value = (defaultVal != null ? defaultVal : (V) str.readObject()); } Entry<K,V> middle = new Entry<K,V>(key, value, null); // color nodes in non-full bottommost level red if (level == redLevel) middle.color = RED; if (left != null) { middle.left = left; left.parent = middle; } if (mid < hi) { Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel, it, str, defaultVal); middle.right = right; right.parent = middle; } return middle; }

对buildFromSorted方法解读

  • buildFromSorted是通过递归将SortedMap中的元素逐个关联
  • buildFromSorted返回middle节点作为root
  • buildFromSorted添加到红黑树中时,只将level == redLevel的节点设为红色。

2.3、TreeMap实现的Serializable接口

TreeMap实现了java.io.Serializable,分别实现了串行读取、写入功能。串行写入函数是writeObject(),它的作用是将TreeMap的容量,所有的Entry都写入到输出流。而串行读取函数是readObject(),它的作用是将TreeMap的容量,所有的Entry依次读出。通过readObject和writeObject能够帮助我们实现TreeMap的串行传输。

private static final long serialVersionUID = 919286545866124006L; /** * Save the state of the <tt>TreeMap</tt> instance to a stream (i.e., * serialize it). * */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out the Comparator and any hidden stuff s.defaultWriteObject(); // Write out size (number of Mappings) s.writeInt(size); // Write out keys and values (alternating) for (Iterator<Map.Entry<K,V>> i = entrySet().iterator(); i.hasNext(); ) { Map.Entry<K,V> e = i.next(); s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } /** * Reconstitute the <tt>TreeMap</tt> instance from a stream (i.e., * deserialize it). */ private void readObject(final java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in the Comparator and any hidden stuff s.defaultReadObject(); // Read in size int size = s.readInt(); buildFromSorted(size, null, s, null); }

2.4、TreeMap实现了Cloneable接口

TreeMap实现了Cloneable接口,即实现了clone()方法。clone()方法的作用很简单,就是克隆一个TreeMap对象并返回。

/*** Returns a shallow copy of this <tt>TreeMap</tt> instance. (The keys and* values themselves are not cloned.)** @return a shallow copy of this map */ public Object clone() { TreeMap<K,V> clone = null; try { clone = (TreeMap<K,V>) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } // Put clone into "virgin" state (except for comparator) clone.root = null; clone.size = 0; clone.modCount = 0; clone.entrySet = null; clone.navigableKeySet = null; clone.descendingMap = null; // Initialize clone with our mappings try { clone.buildFromSorted(size, entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } return clone; }

2.5、TreeMap重要方法的实现原理

TreeMap继承AbstractMap,也即是一个Map。这里对map的主要方法做一个解析

2.5.1、TreeMap的put()方法实现原理

/*** Associates the specified value with the specified key in this map.* If the map previously contained a mapping for the key, the old* value is replaced.*/public V put(K key, V value) { //用t表示二叉树的当前节点 Entry<K,V> t = root; //t为null表示一个空树,即TreeMap中没有任何元素,直接插入 if (t == null) { //将新的key-value键值对创建为一个Entry节点,并将该节点赋予给root root = new Entry<K,V>(key, value, null); size = 1; //修改次数加1 modCount++; return null; } //cmp表示key排序的返回结果 int cmp; Entry<K,V> parent; // 指定的排序算法 Comparator<? super K> cpr = comparator; //如果cpr不为空,则采用给定的排序算法创建TreeMap集合 if (cpr != null) { do { parent = t;//parent指向上次循环后的t cmp = cpr.compare(key, t.key); //cmp返回值小于0,表示新增节点的key小于当前key的值,则以当前节点的左孩子作为新的当前节点 //否则,以当前节点的右孩子作为新的当前节点 if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } //如果cpr为空,则采用默认的排序算法进行创建TreeMap集合 else { if (key == null) throw new NullPointerException(); 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<K,V>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; //上面已经完成了排序二叉树的构建,将新增节点插入该树中的合适位置,下面fixAfterInsertion方法就是对这棵树进行调整、平衡 fixAfterInsertion(e); size++; modCount++; return null; }

上述代码中do代码块实现排序二叉树的核心算法,通过该算法我们可以确认新增节点在该树的正确位置。找到该位置后将插入即可,这样做其实还没有完成,因为我们知道TreeMap的底层实现是红黑树,红黑树是一个平衡排序二叉树,普通的排序二叉树可能会出现失衡的情况,所以下一步就是要进行调整。fixAfterInsertion(e); 调整的过程务必会涉及到红黑树的左旋、右旋、着色三个基本操作。代码如下:

/** From CLR */private void fixAfterInsertion(Entry<K,V> x) { x.color = RED; while (x != null && x != root && x.parent.color == RED) { if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { Entry<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 { Entry<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; }

这段代码中包含了左旋(rotateLeft())、右旋(rotateRight())和着色(setColor)等符合红黑树新增节点的处理过程。

2.5.2、TreeMap的remove()方法实现原理

/*** Removes the mapping for this key from this TreeMap if present.**/public V remove(Object key) { Entry<K,V> p = getEntry(key); if (p == null) return null; V oldValue = p.value; deleteEntry(p); return oldValue; }

通过这段代码可以看出,TreeMap的remove()方法中执行删除的真正方式是deleteEntry()方法。deleteEntry()代码如下:

    /*** Delete node p, and then rebalance the tree.*/private void deleteEntry(Entry<K,V> p) { modCount++;//修改次数 +1; size--;//元素个数 -1 // If strictly internal, copy successor's element to p and then make p // point to successor. /* * 被删除节点的左子树和右子树都不为空,那么就用 p节点的中序后继节点代替 p 节点 * successor(P)方法为寻找P的替代节点。规则是右分支最左边,或者 左分支最右边的节点 * ---------------------(1) */ if (p.left != null && p.right != null) { Entry<K,V> s = successor (p); p.key = s.key; p.value = s.value; p = s; } // p has 2 children // Start fixup at replacement node, if it exists. //replacement为替代节点,如果P的左子树存在那么就用左子树替代,否则用右子树替代 Entry<K,V> replacement = (p.left != null ? p.left : p.right); /* * 删除节点,分为上面提到的三种情况 * -----------------------(2) */ //如果替代节点不为空 if (replacement != null) { // Link replacement to parent //replacement来替代P节点 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; // Null out links so they are OK to use by fixAfterDeletion. p.left = p.right = p.parent = null; // Fix replacement 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; } } }

3、TreeMap的使用场景

我们了解了什么是TreeMap以及它的部分实现原理。那我们该如何运用treeMap呢?TreeMap是Map接口的具体实现,它的应用场景与Map应用场景大体相同。Map用于保存具有"映射关系"的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value。key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较结果总是返回false。 关于Map,我们要从代码复用的角度去理解,java是先实现了Map,然后通过包装了一个所有value都为null的Map就实现了Set集合 Map的这些实现类和子接口中key集的存储形式和Set集合完全相同(即key不能重复) Map的这些实现类和子接口中value集的存储形式和List非常类似(即value可以重复、根据索引来查找) 。TreeMap通常比HashMap、Hashtable要慢(尤其是在插入、删除key-value对时更慢),因为TreeMap底层采用红黑树来管理键值对。但是TreeMap有一个好处就是:TreeMap中的key-value对总是处于有序状态,无须专门进行排序操作。并且虽然TreeMap在插入和删除方面性能比较差,但是在分类处理的时候作用很大,遍历的速度很快。TreeMap的使用示例

/*** 该方法用于获得初始化成员变量sysbasecodeCache,调用remote接口,* 获得所有的cbossBaseCode,并按code_type分* 类,每一类编码存放在一个TreeMap中,并把所有生成的TreeMap作 为m_SysBaseTable的成员保存。* * @return void * @throws */ public static void initSysBaseCode() throws CBossDataAccessException { long start = System.currentTimeMillis(); CBossLogUtil.getLogger(BaseCodeHelper.class).info(">>开始缓存["+CbossBaseCodeDataModule.TABLE_NAME+"]数据..."); // 初始化前先清理缓存 cleancache(); int iCodeType = -999; Map<String, CbossBaseCodeDataModule> treeMap = null; Connection conn = null; try { conn = DataSourceProxy.getConnectionBySchema(CbossBaseCodeDataModule.SCHEMA_NAME); CbossBaseCodeDAO basedao = new CbossBaseCodeDAO(conn); List<CbossBaseCodeDataModule> baseCodes = basedao.selectAll(); for (CbossBaseCodeDataModule cbossBaseCode : baseCodes) { iCodeType = cbossBaseCode.getCodeType(); treeMap = (TreeMap) sysbasecodeCache.get(new Integer(iCodeType)); if (treeMap == null) { treeMap = new TreeMap<String, CbossBaseCodeDataModule>(); sysbasecodeCache.put(new Integer(iCodeType), treeMap); } treeMap.put(cbossBaseCode.getCodeId(), cbossBaseCode); } } catch (Exception e) { throw new CBossDataAccessException(e.getMessage() + "基础代码初始化时出现异常"); } finally { DataSourceProxy.closeConnection(conn); CBossLogUtil.getLogger(BaseCodeHelper.class).info(">>["+CbossBaseCodeDataModule.TABLE_NAME+"]数据缓存完成, cost("+(System.currentTimeMillis() - start)+")ms"); } }

在遍历TreeMap的时候,能够直接确定分类,然后在分类中遍历,最终能够以最快的速度获取想要的结果。

转载于:https://www.cnblogs.com/duanxz/p/4455512.html

TreeMap源码解析相关推荐

  1. Java集合之TreeMap源码解析上篇

    上期回顾 上期我从树型结构谈到了红黑树的概念以及自平衡的各种变化(指路上期←戳),本期我将会对TreeMap结合红黑树理论进行解读. 首先,我们先来回忆一下红黑树的5条基本规则. 1.结点是红色或者黑 ...

  2. Java TreeMap 源码解析

    继上篇文章介绍完了HashMap,这篇文章开始介绍Map系列另一个比较重要的类TreeMap. 大家也许能感觉到,网络上介绍HashMap的文章比较多,但是介绍TreeMap反而不那么多,这里面是有原 ...

  3. TreeMap源码解析。

    /*** 基于红黑树(Red-Black tree)的 NavigableMap 实现.该映射根据其键的自然顺序进行排序,* 或者根据创建映射时提供的Comparator 进行排序,具体取决于使用的构 ...

  4. Java集合系列---TreeMap源码解析(巨好懂!!!)

    TreeMap底层是基于红黑树实现,能实现根据key值对节点进行排序,排序可分为自然排序和定制排序. 自然排序:TreeMap的所有key必须实现Comparable接口, 定制排序:创建TreeMa ...

  5. 2.8 SortedMap接口与TreeMap源码解析

    2.8 SortedMap接口 SortedMap继承自Map接口,是一种有序的Map. public interface SortedMap<K,V> extends Map<K, ...

  6. TreeSet源码解析

    TreeSet概述 所有实现的接口: Serializable, Cloneable, Iterable<E>, Collection<E>, NavigableSet< ...

  7. 死磕 java集合之TreeMap源码分析(一)——红黑树全解析

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 TreeMap使用红黑树存储元素,可以保证元素按key值的大小进行遍历. 继承体系 Tr ...

  8. java treeset原理_Java集合 --- TreeSet底层实现和原理(源码解析)

    概述 文章的内容基于JDK1.7进行分析,之所以选用这个版本,是因为1.8的有些类做了改动,增加了阅读的难度,虽然是1.7,但是对于1.8做了重大改动的内容,文章也会进行说明. TreeSet实现了S ...

  9. TreeMap源码分析,看了都说好

    一.简介 TreeMap最早出现在JDK 1.2中,是 Java 集合框架中比较重要一个的实现.TreeMap 底层基于红黑树实现,可保证在log(n)时间复杂度内完成 containsKey.get ...

最新文章

  1. 小学生学python到底能干什么-小学生都学Python了,你还没用万矿?
  2. 全北现代宣布江苏苏宁中后卫洪正好租借延长1年
  3. 电动垂直起降飞机已经完成测试,就差一张飞行执照即可合法升空
  4. Python Django 参数解包及代码示例
  5. Count Color poj2777 线段树
  6. 软件测试理论入门(一)
  7. Python切片各种情况详解
  8. 公式之美:打通复杂思维的任督二脉
  9. 双11特刊|十年磨一剑,云原生多模数据库Lindorm 2021双11总结
  10. oracle 超市管理系统,SuperManager 超市账单管理系统 JSP + Servlet + Oracle Jsp/ 240万源代码下载- www.pudn.com...
  11. 周鸿祎:谷歌Chrome不是浏览器
  12. metronic 4.5.7开发环境下, 在Windows 10上安装了10.16.0版本的node js之后,导致node sass无法加载...
  13. WiseCloud成为全球首批Kubernetes官方认证平台产品-睿云智合
  14. Java高级工程师需要掌握哪些核心点?
  15. 计算机专业论文周进展300字,论文进展情况记录300字_论文周进展情况记录文库_论文进展情况18篇记录...
  16. UE4编辑器扩展踩坑血泪史
  17. 架设服务器虚拟主机教程,web服务器虚拟主机(服务器搭建虚拟主机教程)
  18. module ‘statsmodels.stats.diagnostic‘ has no attribute ‘het_breushpagan‘
  19. 【云驻共创】华为云数据库之大数据入门与应用(上)
  20. 系统架构演变和远程调用

热门文章

  1. Ubuntu扩大boot空间
  2. HDU——2768 Cat vs. Dog
  3. 学习Machine Leaning In Action(四):逻辑回归
  4. 08-Scrum过程-办公环境 每日立会(Standup Meeting)
  5. 虚拟键码对照表与ASCII对照表的整理
  6. php数据库--mysql优化
  7. 西门子1200如何与c语言通信,S7-1200PLC1214c dc/dc/dc通过profinet以太网和S7-200smartcpu通讯怎么设置?...
  8. git托管怎么使用_使用Git构建和托管网站
  9. 美国 otc 数字货币_美国数字公共图书馆的免费藏书量是第一年的三倍
  10. (13)Node.js 文件流 缓冲 VS 流