The red-black tree into JUC ConcurrentHashMap

ConcurrentHashMap implementation process, which is mentioned at the put operation, if it is found in the list structure element exceeds TREEIFY_THRESHOLD (default 8), the list will be converted into a red-black tree, it is easy to improve search efficiency. code show as below:

if (binCount >= TREEIFY_THRESHOLD)
    treeifyBin(tab, i);
复制代码

Red-black tree

The basic concept look black tree: red-black tree is a balanced binary tree special, mainly with its ordered store data, provide efficient data retrieval, the time complexity is O (lgn). Each node has a red-black tree identification bit indicates the color, black or red, is provided with five kinds of characteristics:

1. 每个节点非红即黑
2. 根节点为黑色
3. 每个叶子节点为黑色。叶子节点为NIL节点,即空节点
4. 如果一个节点为红色,那么它的子节点一定是黑色
5. 从一个节点到该节点的子孙节点的所有路径包含相同个数的黑色节点
复制代码

These five properties, which at the time of the election is particularly important to maintain red-black tree

Red-black tree structure is as follows:

For red-black tree, which includes three steps: L, D, coloring. All five properties do not meet the above "red-black tree" can be adjusted to the regular red-black tree through these three steps.

Rotation

When red-black tree insert and delete operations could damage characteristic red-black tree. In order to keep the properties of red-black trees, red-black tree is required by rotating and re-coloring treatment, wherein the rotating comprises L, D.

L

L diagram below:

L-handling process is relatively simple, the right of children E S E adjusted to the parent's right child left child node S as the adjustment of the E node.

Right-handed

Red-black tree insertion node

Since the list is converted into red-black tree only add operations, coupled with limited space so there will only introduce the red-black tree insert operations, the details of the red-black tree, be grateful if you Google.

During the analysis, we have the following cases as a simple tree, a root node G, has two child nodes P, U, our new node N

The default node is inserted red-black tree red, because if it is black, the red-black tree will destroy the rule 5 (path from one node to all the descendants of the node node comprising the same number of black nodes).

Although the default node is red, red-black tree after insertion can lead to an imbalance. Red-black tree insert major cause of the imbalance is that the color of the current node and its parent node conflict inserted leads (red, contrary to Rule 4: If a node is red, then its child nodes must be black).

To solve this conflict by the above three operations: L, D, recolor. Because conflict is red, then the grandparent certainly exists and is black, but the color uncle node U unsure, you can make the appropriate adjustments according to the color uncle node.

  1. Uncle node U is red

If uncle node is red, then the process becomes relatively simple: change the color G and P, U node, a next FIG.

Of course, this may lead to discoloration of another problem, that is, the parent node and its parent node G GG colors that conflict (Figure II), this will require new node as a node G recursive processing.

  1. Uncle node U black t

If the current node uncle U is black, it is necessary according to the current position of the node N its parent P is determined, is divided into four cases:

N是P的右子节点、P是G的右子节点
N是P的左子节点,P是G的左子节点
N是P的左子节点,P是G的右子节点
N是P的右子节点,P是G的左子节点
复制代码

Called outer case 2 is inserted, the inside of the case 3 and 4 are inserted, the reason for this distinction because their handling is relative.

Outside of the insert

N is to the right child of P, P G is the right child of an example, for handling this situation: L to P as a fulcrum, and then switched colors P and G (P set to black, G is set to red), as follows:

Left outside the case (N is the left child of P, P is the left child of the node G) and the same manner as the above process, the first right-handed, and then re-colored.

Inserted inside

To N is a left child of P, P where G is the right child of an example. Inserted inside the case is slightly more complicated, after one rotation, the coloring can not be adjusted to a red-black tree, the processing as follows: to make only one right-handed, left-handed once again, and then re-coloring, to complete adjustment. Note that the two add dextrose to the node N are not serving as a fulcrum P. Here the two nodes N NIL node is named X, L. as follows:

As for the left medial processing logic is as follows: for the first right-handed and left-handed, and finally colored.

ConcurrentHashMap process of treeifyBin

ConcurrentHashMap the list is converted to a red-black tree is a process of increasing the red-black tree node process. Put in the process, if it is found in the list structure element exceeds TREEIFY_THRESHOLD (default 8), the list will be converted to red-black tree:

if (binCount >= TREEIFY_THRESHOLD)
    treeifyBin(tab, i);
treeifyBin主要的功能就是把链表所有的节点Node转换为TreeNode节点,如下:

private final void treeifyBin(Node<K,V>[] tab, int index) {
    Node<K,V> b; int n, sc;
    if (tab != null) {
        if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
            tryPresize(n << 1);
        else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
            synchronized (b) {
                if (tabAt(tab, index) == b) {
                    TreeNode<K,V> hd = null, tl = null;
                    for (Node<K,V> e = b; e != null; e = e.next) {
                        TreeNode<K,V> p =
                            new TreeNode<K,V>(e.hash, e.key, e.val,
                                              null, null);
                        if ((p.prev = tl) == null)
                            hd = p;
                        else
                            tl.next = p;
                        tl = p;
                    }
                    setTabAt(tab, index, new TreeBin<K,V>(hd));
                }
            }
        }
    }
}
复制代码

Analyzing the first current is smaller than the length of the array Node MIN_TREEIFY_CAPACITY (64), is less than tryPresize expansion process is called a single list element to relieve excessive performance problems. Otherwise, the list will be converted to Node node TreeNode node list, after completing the construction of the red-black tree call setTabAt () construct. Inherit TreeNode Node, as follows:

   static final class TreeNode<K,V> extends Node<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,
                TreeNode<K,V> parent) {
           super(hash, key, val, next);
           this.parent = parent;
       }
	......
}
复制代码

Take the following case as a linked list, with the source code to create analysis ConcurrentHashMap red-black tree:

put 12

12 as the root node, directly programming black to red, the corresponding source code:

next = (TreeNode<K,V>)x.next;
x.left = x.right = null;
if (r == null) {
    x.parent = null;
    x.red = false;
    r = x;
}
复制代码

(Notes: For convenience, here are omitted NIL node behind the same)

put 1

At this time, root root node is not empty, it is necessary to find a suitable position of the insertion node is inserted, the source is as follows:

K k = x.key;
int h = x.hash;
Class<?> kc = null;
for (TreeNode<K,V> p = r;;) {
    int dir, ph;
    K pk = p.key;
    if ((ph = p.hash) > h)
        dir = -1;
    else if (ph < h)
        dir = 1;
    else if ((kc == null &&
              (kc = comparableClassFor(k)) == null) ||
             (dir = compareComparables(kc, k, pk)) == 0)
        dir = tieBreakOrder(k, pk);
        TreeNode<K,V> xp = p;
    if ((p = (dir <= 0) ? p.left : p.right) == null) {
        x.parent = xp;
        if (dir <= 0)
            xp.left = x;
        else
            xp.right = x;
        r = balanceInsertion(r, x);
        break;
    }
}
复制代码

From the above you can see from the processing logic is as follows:

  1. p hash value computing node. dir expressed as to the left or to the right. x represents the node to be inserted, p is compared with the node represents.

  2. Starting from the root node, the calculation and comparison of the hash value of the node p pH, if ph> h, then dir = -1, represent left, taking p = p.left. If p == null insert, if p! = Null, then the comparison continues until a suitable location until the last call balanceInsertion () method of adjusting the red-black tree structure. ph <h, right.

  3. If ph = h, it means that the node "conflict" (and the same HashMap conflict), how to handle it? First call comparableClassFor () key method to determine whether the node implements the Comparable interface if kc! = Null, through compareComparables () method by the compareTo () Comparative vaginal discharge, or returns 0 if, i.e. dir == 0, then call tieBreakOrder () method to compare. tieBreakOrder as follows:

static int tieBreakOrder(Object a, Object b) {
    int d;
    if (a == null || b == null ||
        (d = a.getClass().getName().
         compareTo(b.getClass().getName())) == 0)
        d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
             -1 : 1);
    return d;
}
复制代码

tieBreakOrder () method to compare ultimately by calling System.identityHashCode () method.

After determining the insertion position, insertion, the insertion of the node is likely to break the red-black tree structure, so the caller balanceInsertion () method to adjust the red-black tree structure after insertion.

static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                            TreeNode<K,V> x) {
    x.red = true;       // 所有节点默认插入为红
    for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {

        // x.parent == null,为跟节点,置黑即可
        if ((xp = x.parent) == null) {
            x.red = false;
            return x;
        }
        // x 父节点为黑色,或者x 的祖父节点为空,直接插入返回
        else if (!xp.red || (xpp = xp.parent) == null)
            return root;

        /*
         * x 的 父节点为红色
         * ---------------------
         * x 的 父节点 为 其祖父节点的左子节点
         */
        if (xp == (xppl = xpp.left)) {
            /*
             * x的叔父节点存在,且为红色,颜色交换即可
             * x的父节点、叔父节点变为黑色,祖父节点变为红色
             */
            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);
                    // 左旋之后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 {
            if (xppl != null && xppl.red) {
                xppl.red = false;
                xp.red = false;
                xpp.red = true;
                x = xpp;
            }
            else {
                if (x == xp.left) {
                    root = rotateRight(root, x = xp);
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        root = rotateLeft(root, xpp);
                    }
                }
            }
        }
    }
}
复制代码

Back to Node 1, its parent node is black, namely:

else if (!xp.red || (xpp = xp.parent) == null)
    return root;
复制代码

Direct insert:

put 9

9 as the right child node 1 is inserted, there are red conflicts, this uncle node 9 no. 9 is a parent node 12 is the left child, right child node 9 is a parent node, so that the processing logic is first left, then right-handed, the corresponding code is as follows:

if (x == xp.right) {
    root = rotateLeft(root, x = xp);
    xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
    xp.red = false;
    if (xpp != null) {
        xpp.red = true;
        root = rotateRight(root, xpp);
    }
}
复制代码

Legend changes are as follows:

put 2

2 as the right child node 1 is inserted, red conflict, which cut uncle node red color can be direct,:

if ((xppr = xpp.right) != null && xppr.red) {
    xppr.red = false;
    xp.red = false;
    xpp.red = true;
    x = xpp;
}
复制代码

Legend correspond to:

put 0

1 as the left child node 0 of node insertion, since the parent node is black, red-black tree structure does not break after insertion, can be directly inserted:

put 11

Node 11 as the left child node 12, its parent node 12 is black, and 0 the same reason, direct insert:

put 7

Node 7 is inserted as a right child node 2, red conflict, uncle node 0 which is red, color can be:

put 19

As the right child node 19 to node 12, directly into:

At this point, the whole process has been completed, the final results are as follows:

Guess you like

Origin juejin.im/post/5d8ddc5b6fb9a04e3c04de2b