Java8 HashMap源码阅读
二叉查找树和红黑树
二叉查找树
二叉查找树满足对于任意节点N,其左子树的所有节点的值都小于节点N,右子树的所有节点的值都大于节点N。树的高度为[log2N,N]。查找、插入、删除的时间复杂度最优为O(log2N),最坏为O(N)
红黑树
红黑树性质:
- 每个节点要么是红色,要么是黑色
- 根节点是黑色的,null节点是黑色
- 如果一个节点是红色的,则它的子节点必须是黑色的
- 从一个节点到到null引用的每一条路径必须包含相同数目的黑节点
红黑树的高度最大为2log2(N+1)。查找、插入、删除时间复杂度为O(log2N)
HashMap put 方法
HashMap内部通过数组加链表的方式保存数据。
添加节点(键值对)到HashMap:
- 首先通过 (n - 1) & hash 得到数组索引位置 j,其中 n 为数组长度,hash为键值对中键的哈希值;
- 如果 j 处无节点,则把此节点添加到数组的 j 位置,为链表的根节点;
- 如果 j 处有节点,而且链表不为红黑树结构,则把节点插到 j 处链表的末尾(假设此节点的key在HashMap中不存在),链表插入时间复杂度为O(N);若链表的长度大于某个值(默认为8),而且数组的长度大于某个值(默认为64),把此链表转化为红黑树结构
- 如果 j 处有节点,而且链表为红黑树结构,则把节点插入红黑树结构中,链表插入时间复杂度为O(log2N)
基本哈希表节点,HashMap保存的的数据的基本类型:
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; }
当为红黑树结构时保存的数据类型,TreeNode是上面代码Node的子类,super也即调用的Node的方法。prev字段使链表变成双向链表;red为true表示此节点颜色为红色,否则为黑色。
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; // red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); } }
HashMap put方法,调用putVal
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
putVal方法,部分代码,1 判断数组第一个节点是否和插入数据的key相等,若相等,则把数组第一个节点赋值给e,后面会用新value覆盖e的value。2 如果此链表节点类型为TreeNode,即红黑树结构,则调用TreeNode putTreeVal方法。3 否则遍历节点,如果遍历的节点和要插入数据的key相等,则把此节点赋值给e,否则创建一个新节点并插入链表尾端。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { // 判断第一个节点的key是否和插入的key相等 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // 把节点插入到红黑树结构中 else if (p instanceof TreeNode) e = ((HashMap.TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value); else { // 遍历链表,把节点插入到链表尾端 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st // 把链表转化为红黑树结构 treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; } } } }
TreeNode putTreeVal方法,部分代码,把节点插入红黑树中。1 通过二叉查找树左子树小于父节点、父节点小于右子树的性质遍历到叶子节点,即为null的节点,把新节点插入到此位置。2 调用balanceInserting方法使树结构重新恢复为红黑树结构
final HashMap.TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab, int h, K k, V v) { // 找根节点 HashMap.TreeNode<K,V> root = (parent != null) ? root() : this; // 在红黑树中查找要插入的位置 for (HashMap.TreeNode<K,V> p = root;;) { int dir, ph; K pk; if ((ph = p.hash) > h) dir = -1; else if (ph < h) dir = 1; else if ((pk = p.key) == k || (k != null && k.equals(pk))) return p; HashMap.TreeNode<K,V> xp = p; // 当找到插入的位置时,插入位置为空节点 if ((p = (dir <= 0) ? p.left : p.right) == null) { Node<K,V> xpn = xp.next; HashMap.TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn); if (dir <= 0) xp.left = x; else xp.right = x; xp.next = x; x.parent = x.prev = xp; if (xpn != null) ((HashMap.TreeNode<K,V>)xpn).prev = x; // 插入新节点x后,可能会破坏红黑树的性质,调用balanceInsertion恢复红黑树结构 moveRootToFront(tab, balanceInsertion(root, x)); return null; } } }
TreeNode balanceInserting方法,部分代码。修复红黑树结构参考文章https://tech.meituan.com/2016/12/02/redblack-tree.html
static <K,V> HashMap.TreeNode<K,V> balanceInsertion(HashMap.TreeNode<K,V> root, HashMap.TreeNode<K,V> x) { // 插入节点为红色 x.red = true; for (HashMap.TreeNode<K,V> xp, xpp, xppl, xppr;;) { // 插入节点为根节点 if ((xp = x.parent) == null) { x.red = false; return x; } // 插入节点的父节点为黑色或者为根节点,红黑树结构没有被破坏 else if (!xp.red || (xpp = xp.parent) == null) return root; // 如果父节点为左孩子 if (xp == (xppl = xpp.left)) { // 如果父节点和其兄弟节点都为红色,变换颜色 if ((xppr = xpp.right) != null && xppr.red) { xppr.red = false; xp.red = false; xpp.red = true; x = xpp; } else { // 父节点为红色,父节点的兄弟节点为黑色 // 如果节点X,父节点,祖父节点不再一条直线上,左旋使他们在一条直线上 if (x == xp.right) { root = rotateLeft(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } // 父节点为红色,父节点的兄弟节点为黑色 // 节点X,父节点,祖父节点在一条直线上,右旋 if (xp != null) { xp.red = false; if (xpp != null) { xpp.red = true; root = rotateRight(root, xpp); } } } } // 如果父节点为有孩子,是父节点为左孩子时的镜像操作 else { } } }
若有理解错误的、写错的地方、更好的思路,方法,望各位读者评论指正或指点我,不胜感激
本文主要参考以下文章:
https://tech.meituan.com/2016/12/02/redblack-tree.html
https://www.cnblogs.com/gaochundong/p/binary_search_tree.html#delete-node

浙公网安备 33010602011771号