共同学习Java源代码-数据结构-HashMap(十八)

来源:互联网 发布:什么是编程范式 编辑:程序博客网 时间:2024/06/10 03:53
        final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                                       int h, K k, V v) {
            Class<?> kc = null;
            boolean searched = false;
            TreeNode<K,V> root = (parent != null) ? root() : this;
            for (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;
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0) {
                    if (!searched) {
                        TreeNode<K,V> q, ch;
                        searched = true;
                        if (((ch = p.left) != null &&
                             (q = ch.find(h, k, kc)) != null) ||
                            ((ch = p.right) != null &&
                             (q = ch.find(h, k, kc)) != null))
                            return q;
                    }
                    dir = tieBreakOrder(k, pk);
                }


                TreeNode<K,V> xp = p;
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    Node<K,V> xpn = xp.next;
                    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)
                        ((TreeNode<K,V>)xpn).prev = x;
                    moveRootToFront(tab, balanceInsertion(root, x));
                    return null;
                }
            }

        }

这个方法是插入树节点的方法 

首先定义Class类型临时变量kc 布尔临时变量searched默认为false

然后创建root变量获取根节点 就是判断本节点的父节点是否为空 如果父节点不为空 就调用root方法获取  如果父节点为空 本节点就作为根节点 

然后进入for循环遍历每个节点 循环变量是p 初始值是root

创建临时变量dir ph和pk

将p的哈希值赋给ph 判断ph如果大于参数h dir就是-1 

如果ph小于参数h dir就是1 就是判断传进来的参数的哈希值和这里遍历的节点的哈希值 用来确定传进来的参数添加到节点的哪里

如果传进来的k键和遍历中的节点的键一致或为同一对象 那么直接返回该节点

如果两个哈希值相同 kc为空 且 参数键k的类型不是Comparable 或参数k和本节点的键相比的比值为0  那么判断searched变量是否为false 也就是没进行过搜索 将search变量设为true 就对p的左子树和右子树进行搜索 搜到了和要插入的节点相同的节点 就返回搜到的节点 最后跳出判断searched变量后 调用tieBreakOrder给dir赋值

然后根据dir的值判断 如果dir小于等于0 且p的左节点为空 或dir大于0 且p的右节点为空 

那么将p的下一个节点设置为树节点 通过调用参数map的newTreeNode方法 将新传入的键值对放在本节点和下一个节点之间的节点 操作方式就是本节点的下一个节点作为新节点的下一个节点 新节点根据dir值放在本节点的左子树或右子树中 新节点作为本节点的下一个节点 新节点的上一个节点和父节点都变成成本节点 新节点的下一个节点(曾是本节点的下一个节点)的上一个节点设为新节点

然后将根节点移动到哈希桶数组中 并调用balanceInsertion方法调整根节点

最后返回空值


search变量设为true


        final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                                  boolean movable) {
            int n;
            if (tab == null || (n = tab.length) == 0)
                return;
            int index = (n - 1) & hash;
            TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
            TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
            if (pred == null)
                tab[index] = first = succ;
            else
                pred.next = succ;
            if (succ != null)
                succ.prev = pred;
            if (first == null)
                return;
            if (root.parent != null)
                root = root.root();
            if (root == null || root.right == null ||
                (rl = root.left) == null || rl.left == null) {
                tab[index] = first.untreeify(map);  // too small
                return;
            }
            TreeNode<K,V> p = this, pl = left, pr = right, replacement;
            if (pl != null && pr != null) {
                TreeNode<K,V> s = pr, sl;
                while ((sl = s.left) != null) // find successor
                    s = sl;
                boolean c = s.red; s.red = p.red; p.red = c; // swap colors
                TreeNode<K,V> sr = s.right;
                TreeNode<K,V> pp = p.parent;
                if (s == pr) { // p was s's direct parent
                    p.parent = s;
                    s.right = p;
                }
                else {
                    TreeNode<K,V> sp = s.parent;
                    if ((p.parent = sp) != null) {
                        if (s == sp.left)
                            sp.left = p;
                        else
                            sp.right = p;
                    }
                    if ((s.right = pr) != null)
                        pr.parent = s;
                }
                p.left = null;
                if ((p.right = sr) != null)
                    sr.parent = p;
                if ((s.left = pl) != null)
                    pl.parent = s;
                if ((s.parent = pp) == null)
                    root = s;
                else if (p == pp.left)
                    pp.left = s;
                else
                    pp.right = s;
                if (sr != null)
                    replacement = sr;
                else
                    replacement = p;
            }
            else if (pl != null)
                replacement = pl;
            else if (pr != null)
                replacement = pr;
            else
                replacement = p;
            if (replacement != p) {
                TreeNode<K,V> pp = replacement.parent = p.parent;
                if (pp == null)
                    root = replacement;
                else if (p == pp.left)
                    pp.left = replacement;
                else
                    pp.right = replacement;
                p.left = p.right = p.parent = null;
            }


            TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);


            if (replacement == p) {  // detach
                TreeNode<K,V> pp = p.parent;
                p.parent = null;
                if (pp != null) {
                    if (p == pp.left)
                        pp.left = null;
                    else if (p == pp.right)
                        pp.right = null;
                }
            }
            if (movable)
                moveRootToFront(tab, r);
        }

这个方法是删除树节点的方法

先判断哈希桶数组是否为空 如果为空或没有元素 就直接返回

然后根据传进来的哈希值确定是在哪个桶 用哈希桶数组长度减一 再按位与哈希值的方法计算得出

创建临时变量first 赋为哈希桶数组指定下标的元素 也就是整棵树的根节点 root也赋为这个根节点 再创建一个rl变量 默认为空

创建临时变量succ 赋为next节点 创建临时变量pred 赋为prev节点

判断如果pred为空 也就是前驱节点为空 那么哈希桶数组的下标元素和first元素赋为succ 也就是说要删除的是第一个元素 那么其他元素前移

如果pred不为空 那么pred和succ直接建立关联 pred的next属性为succ succ的prev属性为pred

如果first还是为空 那么说明树为空树 直接返回

如果root节点的父节点不为空 那么调用root方法重新赋给root

如果root为空或root的右子树为空 或root的左子树为空 或root的左子树的左子树为空 那么调用untreeify方法将树变成链表 然后返回

然后创建临时变量p引用本节点 pl引用左节点 pr引用右节点 replcement为临时创建的变量


如果pl和pr都不为空 

进入while循环 创建临时变量s 引用pr 不断判断s的左子树以及左子树的左子树 直到找到一个左子树为空的节点 也就是用右子树的左子树的左子树。。。的左子树 直到一个左子树的左子树为空时 再跳出循环 

然后再将这个s的红与黑属性和本节点的红与黑属性替换

创建临时变量sr 赋为s的右子树 创建临时变量pp 赋为p的父元素

判断如果选出来的s是pr 也就是s是本节点的直接右子树 (这个直接的右子树的左子树为空) 那么将s和p互换 s成为本节点p的父节点 s的右子树为p

如果s不是p的直接右子树 

创建临时变量sp 赋为s的直接父元素 

将sp赋给p的parent 也就是将sp变成p的父元素 判断sp不为空的话 再判断如果s是sp的左子树 那么p就变成sp的左子树 如果s是sp的右子树 那么p就变成sp的右子树 换句话说 如果s不是p的直接右子树 那么p直接替换s 

然后s的右子树变为pr 就是p的右子树给了s 

此时p的移位暂告一段落 p要么是s的右子树 也就是p是自己右子树的右子树 p要么是p的某个右子树的左子树的左子树。。。的左子树

然后将p的左子树置空

然后将s的右子树变成p的右子树

然后将p的左子树变成s的左子树

然后用s将p替换 s的父节变成p的父节点 如果p本身是个左节点 那么s就替代成为这个左节点 如果p是个右节点 那么s就替代成为这个右节点

判断s的右子树是否为空 如果不为空 replacement赋为s的右子树sr 否则replacement就是p


pl不为空 pr为空的情况 pl就是replacement pr不为空 pl为了的情况pr就是replacement 都为空p就是replacement

如果replacement不是p 那么就将replacement替代p p置空

创建临时变量r 判断如果p是红色 r就是root p是黑色r就是balanceDeletion的返回值

如果replacement是p 那么直接置空p 因为p可能是链条的最末端 没了就没了


如果movable为true 就将r换到哈希桶数组下表那里 就是变成root元素


这个方法看起来很复杂 其实就是条件判断多了些 逻辑就是删了本节点 要找本节点的替代品 

如果本节点左子树为空 那么替代本节点的就是右子树 如果右子树为空 那么替代本节点的就是左子树 如果都为空 那么本节点直接取消

大致流程图如下

 











阅读全文
0 0
原创粉丝点击