1、TreeMap-源码分析-底层数据结构

  1. 1TreeMap的存储结构是红黑树,它包含几个重要的成员变量: root, size, comparator
  2.   1-1root 是红黑数的根节点。它是Entry类型,Entry是红黑数的节点,它包含了红黑数的6个基本组成成分:key(键)、value(值)、left(左孩子)、right(右孩子)、parent(父节点)、color(颜色)。Entry节点根据key进行排序,Entry节点包含的内容为value
  3.   1-2、红黑数排序时,根据Entry中的key进行排序;Entry中的key比较大小是根据比较器comparator来进行判断的。
  4.   1-3size是红黑数中节点的个数。
  5. 2TreeMap主要成员属性:
  6. /**
  7. * 默认情况下comparator为null,这个时候按照key的自然顺序进行排序,
  8. * 如果想让Map的自动排序按照我们自己的规则,这个时候你就需要传递Comparator的实现类
  9. * @serial
  10. */
  11. private final Comparator<? super K> comparator;
  12. /**
  13. * TreeMap的存储结构既然是红黑树,那么必然会有唯一的根节点。
  14. */
  15. private transient Entry<K,V> root;
  16. /**
  17. * Map中key-value对的数量,也即是红黑树中节点Entry的数量
  18. */
  19. private transient int size = 0;
  20. /**
  21. * 红黑树结构的调整次数
  22. */
  23. private transient int modCount = 0;

2、TreeMap-源码分析-构造函数

  1. 1TreeMap提供了四个构造方法,实现了方法的重载。
  2. 1-1、无参构造方法中比较器的值为null,采用自然排序的方法,如果指定了比较器则称之为定制排序.
  3. 1-1-1、自然排序:TreeMap的所有key必须实现Comparable接口,所有的key都是同一个类的对象
  4. 1-1-2、定制排序:创建TreeMap对象传入了一个Comparator对象,该对象负责对TreeMap中所有的key进行排序,采用定制排序不要求Mapkey实现Comparable接口。
  5. 2、源码代码:
  6. /**
  7. * 默认构造函数,按照key的自然顺序排列。
  8. * 插入到映射中的所有键都必须实现comparable接口。
  9. */
  10. public TreeMap() {
  11. comparator = null;
  12. }
  13. /**
  14. * 传递Comparator具体实现,按照该实现规则进行排序
  15. * 插入到映射中的所有键都必须实现comparable接口。
  16. */
  17. public TreeMap(Comparator<? super K> comparator) {
  18. this.comparator = comparator;
  19. }
  20. /**
  21. * 传递一个map实体构建TreeMap,按照默认规则排序
  22. * 插入到映射中的所有键都必须实现comparable接口。
  23. */
  24. public TreeMap(Map<? extends K, ? extends V> m) {
  25. comparator = null;
  26. putAll(m);
  27. }
  28. /**
  29. * 传递一个map实体构建TreeMap,按照传递的map的排序规则进行排序。
  30. * 这个方法以线性时间运行[涉及线性时间排序算法]。
  31. */
  32. public TreeMap(SortedMap<K, ? extends V> m) {
  33. comparator = m.comparator();
  34. try {
  35. buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
  36. } catch (java.io.IOException cannotHappen) {
  37. } catch (ClassNotFoundException cannotHappen) {
  38. }
  39. }

3、TreeMap-源码分析-添加元素

  1. 1、添加元素,调用的.put()方法,TreeMapput方法
  2. 2、.put()方法源码分析:
  3. public V put(K key, V value) {
  4. Entry<K,V> t = root;
  5. /**
  6. * 如果根节点都为null,还没建立起来红黑树,我们先new Entry并赋值给root把红黑树建立起来,这个时候红黑树中已经有一个节点了,同时修改操作+1。
  7. */
  8. if (t == null) {
  9. compare(key, key); // type (and possibly null) check
  10. root = new Entry<>(key, value, null);
  11. size = 1;
  12. modCount++;
  13. return null;
  14. }
  15. //如果节点不为null,定义一个cmp,这个变量用来进行二分查找时的比较;
  16. int cmp;
  17. //定义parent,是new Entry时必须要的参数
  18. Entry<K,V> parent;
  19. // cpr表示有无自己定义的排序规则,分两种情况遍历执行
  20. Comparator<? super K> cpr = comparator;
  21. //有自定义的排序规则的情况。
  22. if (cpr != null) {
  23. /**
  24. * 从root节点开始遍历,通过二分查找逐步向下找
  25. * 第一次循环:从根节点开始,这个时候parent就是根节点,然后通过自定义的排序算法
  26. * cpr.compare(key, t.key)比较传入的key和根节点的key值,如果传入的key<root.key,那么
  27. * 继续在root的左子树中找,从root的左孩子节点(root.left)开始:如果传入的key>root.key,
  28. * 那么继续在root的右子树中找,从root的右孩子节点(root.right)开始;如果恰好key==root.key,
  29. * 那么直接根据root节点的value值即可。
  30. * 后面的循环规则一样,当遍历到的当前节点作为起始节点,逐步往下找
  31. *
  32. * 需要注意的是:这里并没有对key是否为null进行判断,建议自己的实现Comparator时应该要考虑在内
  33. */
  34. do {
  35. parent = t;
  36. cmp = cpr.compare(key, t.key);
  37. if (cmp < 0)
  38. t = t.left;
  39. else if (cmp > 0)
  40. t = t.right;
  41. else
  42. return t.setValue(value);
  43. } while (t != null);
  44. }
  45. else {
  46. //当默认排序时,key值是不能为null的,为null会抛出异常
  47. if (key == null)
  48. throw new NullPointerException();
  49. @SuppressWarnings("unchecked")
  50. Comparable<? super K> k = (Comparable<? super K>) key;
  51. //通过二分查找
  52. do {
  53. parent = t;
  54. cmp = k.compareTo(t.key);
  55. if (cmp < 0)
  56. t = t.left;
  57. else if (cmp > 0)
  58. t = t.right;
  59. else
  60. return t.setValue(value);
  61. } while (t != null);
  62. }
  63. /**
  64. * 执行到这里,说明前面并没有找到相同的key,节点已经遍历到最后了,我们只需要new一个Entry放到
  65. * parent下面即可,但放到左子节点上还是右子节点上,就需要按照红黑树的规则来。
  66. */
  67. Entry<K,V> e = new Entry<>(key, value, parent);
  68. if (cmp < 0)
  69. parent.left = e;
  70. else
  71. parent.right = e;
  72. /**
  73. * 节点加进去了,红黑树,一般情况下加入节点都会对红黑树的结构造成破坏,
  74. * 我们需要通过一些操作来进行自动平衡处置,如【变色】【左旋】【右旋】
  75. */
  76. fixAfterInsertion(e);
  77. size++;
  78. modCount++;
  79. return null;
  80. }
  81. /**
  82. 红黑树插入时自平衡调整的逻辑:
  83. 无需调整
  84. 当父节点为黑色时插入子节点
  85. 【变色】即可实现平衡
  86. 空树插入根节点,将根节点红色变为黑色
  87. 父节点和叔父节点都为红色
  88. 【旋转+变色】才可实现平衡
  89. 父节点为红色左节点,叔父节点为黑色,插入左子节点,那么通过【左左节点旋转】
  90. 父节点为红色左节点,叔父节点为黑色,插入右子节点,那么通过【左右节点旋转】
  91. 父节点为红色右节点,叔父节点为黑色,插入左子节点,那么通过【右左节点旋转】
  92. 父节点为红色右节点,叔父节点为黑色,插入右子节点,那么通过【右右节点旋转】 -
  93. */
  94. /** From CLR */
  95. private void fixAfterInsertion(Entry<K,V> x) {
  96. //新插入的节点为红色节点
  97. x.color = RED;
  98. //父节点为黑色时,并不需要进行树结构调整,只有当父节点为红色时,才需要调整平衡
  99. while (x != null && x != root && x.parent.color == RED) {
  100. //如果父节点是左节点
  101. if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
  102. //获取x节点的父节点的兄弟节点,判断出x节点的父节点为左节点,所以直接取右节点
  103. Entry<K,V> y = rightOf(parentOf(parentOf(x)));
  104. //如果叔父节点为红色,对应于"父节点和叔父节点都为红色",此时通过变色即可实现平衡
  105. if (colorOf(y) == RED) {
  106. setColor(parentOf(x), BLACK); //父节点设置为黑色,
  107. setColor(y, BLACK); //叔父节点设置为黑色,
  108. setColor(parentOf(parentOf(x)), RED); //祖父节点设置为红色
  109. x = parentOf(parentOf(x)); //x=p.parent.parent
  110. } else {//如果叔父节点为黑色,则插入右子节点,通过【左右节点旋转】
  111. //先进行父节点左旋
  112. if (x == rightOf(parentOf(x))) {
  113. x = parentOf(x);
  114. //进行父节点左旋
  115. rotateLeft(x);
  116. }
  117. //设置父节点和祖父节点颜色
  118. setColor(parentOf(x), BLACK);
  119. setColor(parentOf(parentOf(x)), RED);
  120. //进行祖父节点右旋
  121. rotateRight(parentOf(parentOf(x)));
  122. }
  123. } else {//父节点是右节点的情况,且颜色为红色
  124. //获取x节点的父节点的兄弟节点,判断出x节点的父节点为右节点,所以直接取左节点
  125. Entry<K,V> y = leftOf(parentOf(parentOf(x)));
  126. //叔父节点,是一个左节点,颜色为红色时,符合"父节点和叔父节点都为红色"
  127. if (colorOf(y) == RED) {
  128. setColor(parentOf(x), BLACK);
  129. setColor(y, BLACK);
  130. setColor(parentOf(parentOf(x)), RED);
  131. x = parentOf(parentOf(x));
  132. } else {//如果插入节点是黑色,插入的是左子节点,通过【右左节点旋转】
  133. if (x == leftOf(parentOf(x))) {
  134. //先进行父节点右旋
  135. x = parentOf(x);
  136. rotateRight(x);
  137. }
  138. setColor(parentOf(x), BLACK);
  139. setColor(parentOf(parentOf(x)), RED);
  140. //进行祖父节点左旋
  141. rotateLeft(parentOf(parentOf(x)));
  142. }
  143. }
  144. }
  145. //根节点必须为黑色
  146. root.color = BLACK;
  147. }
  148. /**
  149. From CLR,左旋的方法逻辑
  150. 左旋规则:逆时针旋转两个节点,让一个节点被其右子节点取代,而该节点成为右子节点的左子节点
  151. [!!!]方法里的= 赋值是指 指针的指向。
  152. */
  153. private void rotateLeft(Entry<K,V> p) {
  154. if (p != null) {
  155. /**
  156. * 断开当前节点p与其右子节点的关联,重新将节点p的右子节点的地址指向节点p的右子节点的左子节点
  157. * 这个时候节点r没有父节点
  158. */
  159. Entry<K,V> r = p.right;
  160. p.right = r.left;
  161. if (r.left != null)
  162. //将节点p作为节点r的父节点
  163. r.left.parent = p;2
  164. //将节点p的父节点和r的父节点指向同一处
  165. r.parent = p.parent;
  166. //p的父节点为null,则将节点r设置为root
  167. if (p.parent == null)
  168. root = r;
  169. //如果节点p是左子节点,则将该左子节点替换为节点r
  170. else if (p.parent.left == p)
  171. p.parent.left = r;
  172. else
  173. //如果节点p为右子节点,则将该右子节点替换为节点r
  174. p.parent.right = r;
  175. //重新建立p与r的关系
  176. r.left = p;
  177. p.parent = r;
  178. }
  179. }
  180. /** From CLR,右旋的方法逻辑 */
  181. private void rotateRight(Entry<K,V> p) {
  182. if (p != null) {
  183. Entry<K,V> l = p.left;
  184. p.left = l.right;
  185. if (l.right != null) l.right.parent = p;
  186. l.parent = p.parent;
  187. if (p.parent == null)
  188. root = l;
  189. else if (p.parent.right == p)
  190. p.parent.right = l;
  191. else p.parent.left = l;
  192. l.right = p;
  193. p.parent = l;
  194. }
  195. }

3-1、TreeMap-源码分析-添加元素-左旋代码逻辑-示意图


容器(Map)-TreeMap-源码分析 - 图2

3-2、TreeMap-源码分析-添加元素-put代码流程逻辑-示意图


容器(Map)-TreeMap-源码分析 - 图3

4、TreeMap-源码分析-获取元素

  1. 1、获取元素-.get()方法:
  2. 2、源代码:
  3. // 获取"键(key)"对应的"值(value)"
  4. public V get(Object key) {
  5. Entry<K,V> p = getEntry(key);
  6. return (p==null ? null : p.value);
  7. }
  8. // 获取"键"为key的节点(p)
  9. /**
  10. * 默认情况下,即:比较器为空
  11. * 从root节点开始遍历,通过二分查找逐步向下找
  12. * 第一次循环:从根节点开始,这个时候parent就是根节点,然后通过k.compareTo(p.key)比较传入的key和
  13. * 根节点的key值;
  14. * 如果传入的key<root.key, 那么继续在root的左子树中找,从root的左孩子节点(root.left)开始;
  15. * 如果传入的key>root.key, 那么继续在root的右子树中找,从root的右孩子节点(root.right)开始;
  16. * 如果恰好key==root.key,那么直接根据root节点的value值即可。
  17. * 后面的循环规则一样,当遍历到的当前节点作为起始节点,逐步往下找
  18. */
  19. final Entry<K,V> getEntry(Object key) {
  20. // Offload comparator-based version for sake of performance
  21. //比较器不为空的情况
  22. if (comparator != null)
  23. return getEntryUsingComparator(key);
  24. if (key == null)
  25. throw new NullPointerException();
  26. @SuppressWarnings("unchecked")
  27. Comparable<? super K> k = (Comparable<? super K>) key;
  28. Entry<K,V> p = root;
  29. while (p != null) {
  30. int cmp = k.compareTo(p.key);
  31. if (cmp < 0)
  32. p = p.left;
  33. else if (cmp > 0)
  34. p = p.right;
  35. else
  36. return p;
  37. }
  38. return null;
  39. }
  40. //使用自定义比较器,获取"键"为key的节点(p)
  41. /**
  42. * 从root节点开始遍历,通过二分查找逐步向下找:
  43. * 第一次循环:从根节点开始,这个时候parent就是根节点,然后通过自定义的排序算法
  44. * cpr.compare(key, t.key)比较传入的key和根节点的key值,
  45. * 如果传入的key<root.key,那么继续在root的左子树中找,从root的左孩子节点(root.left)开始:
  46. * 如果传入的key>root.key,那么继续在root的右子树中找,从root的右孩子节点(root.right)开始;
  47. * 如果恰好key==root.key,那么直接根据root节点的value值即可。
  48. * 后面的循环规则一样,当遍历到的当前节点作为起始节点,逐步往下找
  49. */
  50. final Entry<K,V> getEntryUsingComparator(Object key) {
  51. @SuppressWarnings("unchecked")
  52. K k = (K) key;
  53. Comparator<? super K> cpr = comparator;
  54. if (cpr != null) {
  55. Entry<K,V> p = root;
  56. while (p != null) {
  57. int cmp = cpr.compare(k, p.key);
  58. if (cmp < 0)
  59. p = p.left;
  60. else if (cmp > 0)
  61. p = p.right;
  62. else
  63. return p;
  64. }
  65. }
  66. return null;
  67. }

5、TreeMap-源码分析-删除元素

  1. 1、删除元素:.remove()方法
  2. 2、源代码:
  3. // 先是找到这个节点,直接调用了.getEntry(Object key)
  4. public V remove(Object key) {
  5. // 先是找到这个节点
  6. Entry<K,V> p = getEntry(key);
  7. if (p == null)
  8. return null;
  9. V oldValue = p.value;
  10. //找到后的删除操作
  11. deleteEntry(p);
  12. return oldValue;
  13. }
  14. /**
  15. 删除操作的原理:
  16. 1.删除的是根节点,则直接将根节点置为null;
  17. 2.待删除节点的左右子节点都为null,删除时将该节点置为null;
  18. 3.待删除节点的左右子节点有一个有值,则用有值的节点替换该节点即可;
  19. 4.待删除节点的左右子节点都不为null,则找前驱或者后继,将前驱或者后继的值复制到该节点中,然后删除前驱或者后继(前驱:左子树中值最大的节点,后继:右子树中值最小的节点)
  20. */
  21. private void deleteEntry(Entry<K,V> p) {
  22. modCount++;
  23. size--;
  24. //当左右子节点都不为null时,通过successor(p)遍历红黑树找到前驱或者后继
  25. //前驱:左子树中值最大的节点,后继:右子树中值最小的节点
  26. if (p.left != null && p.right != null) {
  27. //一个节点的按次序排序后的下一个节点
  28. Entry<K,V> s = successor(p);
  29. //将前驱或者后继的key和value复制到当前节点p中,然后删除节点s(通过将节点p引用指向s)
  30. p.key = s.key;
  31. p.value = s.value;
  32. p = s;
  33. } // p has 2 children
  34. /**
  35. * 至少有一个子节点不为null,直接用这个有值的节点替换掉当前节点,给replacement的parent属性赋值,给parent节点的left属性和right属性赋值,同时要记住叶子节点必须为null,然后用fixAfterDeletion方法
  36. * 进行自平衡处理
  37. */
  38. Entry<K,V> replacement = (p.left != null ? p.left : p.right);
  39. if (replacement != null) {
  40. // Link replacement to parent
  41. //将待删除节点的子节点挂到待删除节点的父节点上。
  42. replacement.parent = p.parent;
  43. if (p.parent == null)
  44. root = replacement;
  45. else if (p == p.parent.left)
  46. p.parent.left = replacement;
  47. else
  48. p.parent.right = replacement;
  49. // Null out links so they are OK to use by fixAfterDeletion.
  50. p.left = p.right = p.parent = null;
  51. /**
  52. * p如果是红色节点的话,那么其子节点replacement必然为红色的,并不影响红黑树的结构
  53. * 但如果p为黑色节点的话,那么其父节点以及子节点都可能是红色的,那么很明显可能会存在红色相连的情况,因此需要进行自平衡的调整
  54. */
  55. if (p.color == BLACK)
  56. //进行删除后自平衡的调整
  57. fixAfterDeletion(replacement);
  58. } else if (p.parent == null) { // return if we are the only node.
  59. root = null;
  60. } else { // No children. Use self as phantom replacement and unlink.
  61. /**
  62. * 如果p节点为黑色,那么p节点删除后,就可能违背每个节点到其叶子节点路径上黑色节点数量一致的规则,因此需要进行自平衡的调整
  63. */
  64. if (p.color == BLACK)
  65. //进行删除后自平衡的调整
  66. fixAfterDeletion(p);
  67. if (p.parent != null) {
  68. if (p == p.parent.left)
  69. p.parent.left = null;
  70. else if (p == p.parent.right)
  71. p.parent.right = null;
  72. p.parent = null;
  73. }
  74. }
  75. }
  76. //进行删除后自平衡的调整
  77. private void fixAfterDeletion(Entry<K,V> x) {
  78. /**
  79. * 当节点x不是root节点且颜色为黑色时
  80. */
  81. while (x != root && colorOf(x) == BLACK) {
  82. /**
  83. * 分节点x是否为左右节点两种情况
  84. */
  85. if (x == leftOf(parentOf(x))) {
  86. Entry<K,V> sib = rightOf(parentOf(x));
  87. /**
  88. * 场景1:当x是左黑色节点,兄弟节点sib是红色节点
  89. * 兄弟节点由红转黑,父节点由黑转红,按父节点左旋,
  90. * 左旋后树的结构变化了,这时重新赋值sib,这个时候sib指向了x的兄弟节点
  91. */
  92. if (colorOf(sib) == RED) {
  93. setColor(sib, BLACK);
  94. setColor(parentOf(x), RED);
  95. rotateLeft(parentOf(x));
  96. sib = rightOf(parentOf(x));
  97. }
  98. /**
  99. * 场景2:节点x、x的兄弟节点sib、sib的左子节点和右子节点都为黑色时,需要将该节点sib由黑变红,同时将x指向当前x的父节点
  100. */
  101. if (colorOf(leftOf(sib)) == BLACK &&
  102. colorOf(rightOf(sib)) == BLACK) {
  103. setColor(sib, RED);
  104. x = parentOf(x);
  105. } else {
  106. /**
  107. * 场景3:节点x、x的兄弟节点sib、sib的右子节点都为黑色,sib的左子节点为红色时,
  108. * 需要将sib左子节点设置为黑色,sib节点设置为红色,同时按sib右旋,再将sib指向x的
  109. * 兄弟节点
  110. */
  111. if (colorOf(rightOf(sib)) == BLACK) {
  112. setColor(leftOf(sib), BLACK);
  113. setColor(sib, RED);
  114. rotateRight(sib);
  115. sib = rightOf(parentOf(x));
  116. }
  117. /**
  118. * 场景4:节点x、x的兄弟节点sib都为黑色,而sib的左右子节点都为红色或者右子节点为红色、左子节点为黑色,此时需要将sib节点的颜色设置成和x的父节点p相同的颜色,
  119. * 设置x的父节点为黑色,设置sib右子节点为黑色,左旋x的父节点p,然后将x赋值为root
  120. */
  121. setColor(sib, colorOf(parentOf(x)));
  122. setColor(parentOf(x), BLACK);
  123. setColor(rightOf(sib), BLACK);
  124. rotateLeft(parentOf(x));
  125. x = root;
  126. }
  127. } else { // symmetric ,x是右节点的情况
  128. Entry<K,V> sib = leftOf(parentOf(x));
  129. if (colorOf(sib) == RED) {
  130. setColor(sib, BLACK);
  131. setColor(parentOf(x), RED);
  132. rotateRight(parentOf(x));
  133. sib = leftOf(parentOf(x));
  134. }
  135. if (colorOf(rightOf(sib)) == BLACK &&
  136. colorOf(leftOf(sib)) == BLACK) {
  137. setColor(sib, RED);
  138. x = parentOf(x);
  139. } else {
  140. if (colorOf(leftOf(sib)) == BLACK) {
  141. setColor(rightOf(sib), BLACK);
  142. setColor(sib, RED);
  143. rotateLeft(sib);
  144. sib = leftOf(parentOf(x));
  145. }
  146. setColor(sib, colorOf(parentOf(x)));
  147. setColor(parentOf(x), BLACK);
  148. setColor(leftOf(sib), BLACK);
  149. rotateRight(parentOf(x));
  150. x = root;
  151. }
  152. }
  153. }
  154. setColor(x, BLACK);
  155. }

5-1、TreeMap-源码分析-删除元素后自平衡-情况

  1. 1、删除元素后自平衡-情况:
  2. 1-1、当x是左黑色节点,兄弟节点sib是红色节点,需要兄弟节点由红转黑,父节点由黑转红,按父节点左旋,左旋后树的结构变化了,这时重新赋值sib,这个时候sib指向了x的兄弟节点。
  3. 1-2、节点xx的兄弟节点sibsib的左子节点和右子节点都为黑色时,需要将该节点sib由黑变红,同时将x指向当前x的父节点。
  4. 1-3、节点xx的兄弟节点sibsib的右子节点都为黑色,sib的左子节点为红色时,需要将sib左子节点设置为黑色,sib节点设置为红色,同时按sib右旋,再将sib指向x的兄弟节点。
  5. 1-4、节点xx的兄弟节点sib都为黑色,而sib的左右子节点都为红色或者右子节点为红色、左子节点为黑色,此时需要将sib节点的颜色设置成和x的父节点p相同的颜色,设置x的父节点颜色为黑色,设置sib右孩子的颜色为黑色,左旋x的父节点p,然后将x赋值为root

5-1-1、TreeMap-源码分析-删除元素后自平衡-情况1-示意图

容器(Map)-TreeMap-源码分析 - 图4

5-1-2、TreeMap-源码分析-删除元素后自平衡-情况2-示意图

容器(Map)-TreeMap-源码分析 - 图5

5-1-3、TreeMap-源码分析-删除元素后自平衡-情况3-示意图容器(Map)-TreeMap-源码分析 - 图6

5-1-4、TreeMap-源码分析-删除元素后自平衡-情况4-示意图

容器(Map)-TreeMap-源码分析 - 图7

6、TreeMap-源码分析-Entry

  1. 1、静态内部类Entry<K,V>类实现类Map.Entry接口,节点属性:通过left属性可以建立左子树,通过right属性可以建立右子树,通过parent可以往上找到父节点。
  2. 2、源代码:
  3. static final class Entry<K,V> implements Map.Entry<K,V> {
  4. K key; //节点的key值
  5. V value; //节点的value值
  6. Entry<K,V> left; //定义了节点的左节点
  7. Entry<K,V> right; //定义了节点的右节点
  8. Entry<K,V> parent; //定义了节点的父节点,可通过该节点可以反过来往上找到自己的父亲
  9. boolean color = BLACK; //默认情况下为黑色节点,可调整
  10. /**
  11. * 构造器
  12. */
  13. Entry(K key, V value, Entry<K,V> parent) {
  14. this.key = key;
  15. this.value = value;
  16. this.parent = parent;
  17. }
  18. /**
  19. * 获取节点的key值
  20. */
  21. public K getKey() {
  22. return key;
  23. }
  24. /**
  25. * 获取节点的value值
  26. */
  27. public V getValue() {
  28. return value;
  29. }
  30. /**
  31. * 用新值替换当前值,并返回当前值(旧值)
  32. */
  33. public V setValue(V value) {
  34. V oldValue = this.value;
  35. this.value = value;
  36. return oldValue;
  37. }
  38. public boolean equals(Object o) {
  39. if (!(o instanceof Map.Entry))
  40. return false;
  41. Map.Entry<?,?> e = (Map.Entry<?,?>)o;
  42. return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
  43. }
  44. public int hashCode() {
  45. int keyHash = (key==null ? 0 : key.hashCode());
  46. int valueHash = (value==null ? 0 : value.hashCode());
  47. return keyHash ^ valueHash;
  48. }
  49. public String toString() {
  50. return key + "=" + value;
  51. }
  52. }

6-1、Entry类-实现结构图


容器(Map)-TreeMap-源码分析 - 图8

7、TreeMap-源码分析-带SortedMap的构造函数

  1. 1、带SortedMap的构造函数,SortedMap会成为TreeMap的子集,SortedMap是一个有序的Map,我们通过buildFromSorted()来创建对应的Map
  2. 2、源代码:
  3. public TreeMap(SortedMap<K, ? extends V> m) {
  4. comparator = m.comparator();
  5. try {
  6. buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
  7. } catch (java.io.IOException cannotHappen) {
  8. } catch (ClassNotFoundException cannotHappen) {
  9. }
  10. }
  11. // 根据已经一个排好序的map创建一个TreeMap
  12. // 将map中的元素逐个添加到TreeMap中,并返回map的中间元素作为根节点。
  13. private void buildFromSorted(int size, Iterator<?> it,
  14. java.io.ObjectInputStream str,
  15. V defaultVal)
  16. throws java.io.IOException, ClassNotFoundException {
  17. this.size = size;
  18. root = buildFromSorted(0, 0, size-1, computeRedLevel(size),
  19. it, str, defaultVal);
  20. }
  21. @SuppressWarnings("unchecked")
  22. private final Entry<K,V> buildFromSorted(int level, int lo, int hi,
  23. int redLevel,
  24. Iterator<?> it,
  25. java.io.ObjectInputStream str,
  26. V defaultVal)
  27. throws java.io.IOException, ClassNotFoundException {
  28. /*
  29. * Strategy: The root is the middlemost element. To get to it, we
  30. * have to first recursively construct the entire left subtree,
  31. * so as to grab all of its elements. We can then proceed with right
  32. * subtree.
  33. *
  34. * The lo and hi arguments are the minimum and maximum
  35. * indices to pull out of the iterator or stream for current subtree.
  36. * They are not actually indexed, we just proceed sequentially,
  37. * ensuring that items are extracted in corresponding order.
  38. */
  39. if (hi < lo) return null;
  40. // 获取中间元素,右移运算,相当于/2
  41. int mid = (lo + hi) >>> 1;
  42. Entry<K,V> left = null;
  43. // 若lo小于mid,则递归调用获取(middle的)左节点
  44. if (lo < mid)
  45. left = buildFromSorted(level+1, lo, mid - 1, redLevel,
  46. it, str, defaultVal);
  47. // extract key and/or value from iterator or stream
  48. // 获取middle节点对应的key和value
  49. K key;
  50. V value;
  51. if (it != null) {
  52. if (defaultVal==null) {
  53. Map.Entry<?,?> entry = (Map.Entry<?,?>)it.next();
  54. key = (K)entry.getKey();
  55. value = (V)entry.getValue();
  56. } else {
  57. key = (K)it.next();
  58. value = defaultVal;
  59. }
  60. } else { // use stream
  61. key = (K) str.readObject();
  62. value = (defaultVal != null ? defaultVal : (V) str.readObject());
  63. }
  64. // 创建middle节点
  65. Entry<K,V> middle = new Entry<>(key, value, null);
  66. // color nodes in non-full bottommost level red
  67. // 若当前节点的深度=红色节点的深度,则将节点着色为红色。
  68. if (level == redLevel)
  69. middle.color = RED;
  70. // 设置middle为left的父亲,left为middle的左孩子
  71. if (left != null) {
  72. middle.left = left;
  73. left.parent = middle;
  74. }
  75. // 递归调用获取(middle的)右孩子。
  76. if (mid < hi) {
  77. Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel,
  78. it, str, defaultVal);
  79. // 设置middle为left的父亲,left为middle的左孩子
  80. middle.right = right;
  81. right.parent = middle;
  82. }
  83. return middle;
  84. }

8、TreeMap-源码分析-Entry相关函数

  1. 1Entry相关函数:TreeMap firstEntry()、 lastEntry()、 lowerEntry()、 higherEntry()、 floorEntry()、 ceilingEntry()、 pollFirstEntry() pollLastEntry()
  2. 2firstEntry()和getFirstEntry()的源代码:都是用于获取第一个节点
  3. /**
  4. 防止用户修改返回的Entry。getFirstEntry()返回的Entry是可以被修改的,但是经过firstEntry()返回的Entry不能被修改,只可以读取Entry的key值和value值。
  5. */
  6. //firstEntry() 是对外接口,对firstEntry()返回的Entry对象只能进行getKey()、getValue()等读取操作。
  7. public Map.Entry<K,V> firstEntry() {
  8. return exportEntry(getFirstEntry());
  9. }
  10. //getFirstEntry()是内部接口,而对getFirstEntry()返回的对象除了可以进行读取操作之后,还可以通过setValue()修改值。
  11. final Entry<K,V> getFirstEntry() {
  12. Entry<K,V> p = root;
  13. if (p != null)
  14. while (p.left != null)
  15. p = p.left;
  16. return p;
  17. }
  18. //.exportEntry(): 新建一个AbstractMap.SimpleImmutableEntry类型的对象,并返回。
  19. static <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> e) {
  20. return (e == null) ? null :
  21. new AbstractMap.SimpleImmutableEntry<>(e);
  22. }

9、TreeMap-源码分析-其他相关函数

  1. 1TreeMap实现的Cloneable接口,即实现了clone()方法。.clone()方法的作用很简单,就是克隆一个TreeMap对象并返回。
  2. 2TreeMap实现java.io.Serializable,分别实现了串行读取、写入功能。
  3. 2-1、串行写入函数是writeObject(),它的作用是将TreeMap的“容量,所有的Entry”都写入到输出流中。
  4. 2-2、而串行读取函数是readObject(),它的作用是将TreeMap的“容量、所有的Entry”依次读出。
  5. 2-3readObject() writeObject() 正好是一对,通过它们,我能实现TreeMap的串行传输。
  6. 3TreeMap实现的NavigableMap接口-API-简介:
  7. 3-1descendingMap() 的作用是返回当前TreeMap的反向的TreeMap。所谓反向,就是排序顺序和原始的顺序相反。
  8. 3-2NavigableSubMap,它是一个抽象集合类,为2个子类--"(升序)AscendingSubMap""(降序)DescendingSubMap"而服务。