Map
在 Java Collections Framework 中设计相关知识点比较多的数据结构,不管是工做仍是面试中都会被频繁的涉及到。经过学习 Map
的源码,咱们可以深刻理解至关部分的数据结构知识和编码技巧。在接下来的几篇文章中会介绍一些数据结构的知识,但愿你们不会以为无聊,由于这部分的能力才是做为程序员的核心能力。同时这部分的知识其实也不是那么高深,我会试着用最简单明了的方法帮你理解。node
Map
数据结构的特色很明显,容许咱们使用 key 来存储和读取元素,而且不容许重复的 key。而不一样 Map
的实现对于 key 的顺序处理是不一致的。例如 HashMap
没法保证 key 的顺序,而 TreeMap
则是按照 key 实现的 Comparator
接口方法来肯定顺序的。程序员
Map
上也定义了每个 key-value 对应的数据必须实现 Entry
接口,上面定义的方法也很简单,基本都是对于 key 和 value 的操做。面试
Map
接口上定义的方法,你们应该都比较熟悉,我这里就不啰嗦了。特别会说起在 JDK8 加入的几个方法,在平常工做中比较实用。若是以前没有 JDK8 使用经验的,能够了解一下。算法
* `getOrDefalut(Object key, V defaultValue)`:当 `Map` 中有 key 对应的 value 时返回 `Map` 中的 value, 不然返回 `defaultValue` 。 * `V compute(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction)`:将 `Map` 中的 key 和对应的 value 做为参数,调用 `remappingFunction` 方法得到 newValue,若是 newValue 不为 null,则替换原来的 value。 * `V putIfAbsent(K key, V value)`:若是当前 `Map` 中没有 key 对应的 value,则执行 put 操做。 * `V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)`:若是当前没有 key 对应的 value,则将参数 value 放入 `Map` 中,不然将原来 key 对应的 value 和新的 value 做为参数调用 `remappingFunction` ,将结果 put 入 `Map` 中。
这些方法的实现都在 Map
接口中,使用的 JDK8 新增的 default
关键字以保持向下兼容,具体的代码很是简单,这里就不啰嗦了。安全
相对 HashMap
而言,TreeMap
涉及的知识点更少些,适合做为熟悉 Map
数据结构的敲门砖。先来看看它的类图:微信
能够看到 TreeMap
继承了 AbstractMap
,并实现了 NavigableMap
接口。AbstractMap
上提供了部分模版方法,便于开发人员实现本身的 Map
,而 NavigableMap
提供了相似以前说起的 NavigableSet
的那些方法,可以返回某个范围的 key 或是 value。因此咱们直接来看 TreeMap
的具体实现细节。数据结构
从一开头的注释中能够得知,TreeMap
是经过红黑树这一数据结构实现的,所以它可以保证 containsKey
,get
,put
,remove
的时间复杂度为 log(n)
。而一样的,TreeMap
也不是线程安全的。因此真正理解 TreeMap
的关键在于了解和掌握红黑树的这一数据结构。因此接下来的部分,我先会花些篇幅帮助你复习一下红黑树的特性,不要以为这是个很难任务,我保证你看完系列文章后必定可以用 Java 手写红黑树。app
用一句话来讲红黑树是一种平衡二叉树。二叉树的概念你们应该都知道,即每一个父节点拥有不超过两个子节点的树。而平衡的意思是左右子树的高度相差不超过一。同时全部左边子树的值都比当前节点小,而右边子树的值都比当前节点大。咱们来看一下几个例子。源码分析
能够看到图1是一棵平衡二叉树,符合咱们以前提到的条件,可是图2就不符合了,由于左右子树的高度相差超过了一。学习
维持平衡的目的在于不让二叉树退化为链表,这样就能够进行二分查找,保持查找的时间复杂度为 log(n)
。但这也意味着在增长节点或是移除节点的时候须要作特殊的操做,以保持整个二叉树的平衡,而红黑树就是这样的一种数据结构。
像以前说起的,红黑树自己是一种平衡二叉树,所以它具有平衡二叉树的全部特色。在此基础上它有一些本身特有的约束条件与特性。
红黑树的每一个节点额外增长了一个颜色的特性,即红色,或是黑色,只能是这两个中的一种,这也是它红黑树名称的由来。咱们看一下 TreeMap
中红黑树节点的源码:
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; }
TreeMap
中每一个节点实现了 Map
中的 Entry 接口,除了表明当前节点的 key,value 两个数据项,还有代码自身节点下的左右子节点的 left
和 right
,以及本身父节点的 parent
。最后就是表明当前节点颜色的 color
,这里的定义一样在 TreeMap
的源码中:
private static final boolean RED = false; private static final boolean BLACK = true;
接着咱们看一下红黑树的特性:
* 根节点(root node) 的颜色始终为黑色 * 两个相邻的节点(即链接在一块儿的节点)不能同为红色 * 从根节点出发,到某个子节点的每条路径上的黑色节点数量都相同
怎么样?够简单吧!接着让咱们看个例子。
从上面的图来看符合咱们以前列出的 3 个特性,请验证一下确保本身对红黑树的概念理解正确。
经过上面的描述你应该已经掌握了红黑树的概念,知道什么是红黑树了。在介绍红黑树的插入以及删除操做以前,咱们先学习三个基本的操做,即颜色变化(color flip),左旋转(left rotation) 和 右旋转(right rotation)。
很是简单,将当前节点颜色变为红色,左右两个子节点的颜色都变为黑色。入下图所示。
用语言来描述可能有些抽象,咱们仍是看一下图片示例,该图片来自 wikipedia。
请多看几遍这幅图,确保本身了解左旋转和右旋转的意义,由于这三个基本操做是后续红黑树插入和删除操做的基础。
此次主要介绍了 Map
和 TreeMap
的一些基础功能,和 TreeMap
之下红黑树的基本概念。红黑树是一种比较重要的高级数据结构,对于开发人员来讲应该是熟练掌握的,本次主要介绍了基本概念和基础的操做。下一篇咱们会涉及红黑树的的插入以及删除操做的具体算法,在进入这部分前,我再次强调请熟练掌握本文的内容,由于这是基础中的基础。
下一篇文章中我会对照 TreeMap
的源码介绍红黑树的算法,但愿你不要错过!
欢迎关注个人微信号「且把金针度与人」,获取更多高质量文章