GSON Source LinkedTreeMap Learning

Keywords: PHP Google JDK less

In the process of learning GSON, LinkedTreeMap, the basic data type of GSON, was discovered, so learning was started.

 private final LinkedTreeMap<String, JsonElement> members =
      new LinkedTreeMap<String, JsonElement>();

LinkedTreeMap, everything is so familiar. In jdk, there are LinkedMap, TreeMap and TreeMap. What is this LinkedTreeMap? As its name implies, it should be a continuous and orderly collection.

package com.google.gson.internal;

Looking at the package name, this is a map that google has implemented itself.

    Node<K, V> parent;
    Node<K, V> left;
    Node<K, V> right;
    Node<K, V> next;
    Node<K, V> prev;
    final K key;
    V value;
    int height;

Look, what is this? A node contains the parent node, the left and right nodes, and the front and back nodes, but don't panic, let's move on.

Next, back to LinkedTreeMap, a comparator NATURAL_ORDER is defined at the very beginning of the class.

  private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
    public int compare(Comparable a, Comparable b) {
      return a.compareTo(b);
    }
  };

This comparator is passed in when constructing LinkHashMap. If the incoming comparator is empty (this class does not implement Comparator), NATURAL_ORDER comparator is used to compare. Here, the difference between Comparator and Comparable is briefly introduced. The classes that implement Comparable interface support sorting (because CompareTo is implemented). If a class does not support sorting (it does not implement the Compareable interface), the Comparator comparator can be used to implement the comparison method.

Why introduce this comparator first, because new nodes and finding nodes in LinkTreeMap all need to use this comparator, in the find() method, first determine whether the root node is empty.

Node<K, V> find(K key, boolean create) {
    Node<K, V> nearest = root;
    if (nearest != null) {
          Comparable<Object> comparableKey = (comparator == NATURAL_ORDER)
          ? (Comparable<Object>) key
          : null;

When the root node is not empty, determine whether the comparator is a LinkTreeMap custom comparator, and if so, use the K-class comparator, otherwise pass null. The null here is primarily a token, deciding whether to use the Compare method or the compareTo method later.

   while (true) {
        comparison = (comparableKey != null)
            ? comparableKey.compareTo(nearest.key)
            : comparator.compare(key, nearest.key);

        // We found the requested key.
        if (comparison == 0) {
          return nearest;
        }

        // If it exists, the key is in a subtree. Go deeper.
        Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right;
        if (child == null) {
          break;
        }

        nearest = child;
      }

In this loop, the possible location of the node to be searched is determined by the return value. When the value ratio is less than the comparative node, the search is continued on his left subtree, and vice versa, the right subtree is queried until it is found or the current object is empty.

Next, if create is false, the find method is terminated, where nearest is the value or root node to be looked up.

If create is true, the node is created, and nearest is null, and nearest is null, there is only one case: the current tree is an empty tree

     created = new Node<K, V>(nearest, key, header, header.prev);
     root = created;

What new Node did

    /** Create a regular entry */
    Node(Node<K, V> parent, K key, Node<K, V> next, Node<K, V> prev) {
      this.parent = parent;
      this.key = key;
      this.height = 1;
      this.next = next;
      this.prev = prev;
      prev.next = this;
      next.prev = this;
    }

When the tree is not an empty tree, it is inserted according to the result of the last comparison.

else {
      created = new Node<K, V>(nearest, key, header, header.prev);
      if (comparison < 0) { // nearest.key is higher
        nearest.left = created;
      } else { // comparison > 0, nearest.key is lower
        nearest.right = created;
      }
      rebalance(nearest, true);
 }

Adjustment.

  private void rebalance(Node<K, V> unbalanced, boolean insert) {
    for (Node<K, V> node = unbalanced; node != null; node = node.parent) {

After entering the method, the node is traversed. The traversal rule is that when the parent node of the node is not empty, infinite loops occur.

      Node<K, V> left = node.left;
      Node<K, V> right = node.right;
      int leftHeight = left != null ? left.height : 0;
      int rightHeight = right != null ? right.height : 0;

      int delta = leftHeight - rightHeight;

Preserve the left and right nodes and the height of the nodes, and calculate the height difference.

if (delta == -2) {
        Node<K, V> rightLeft = right.left;
        Node<K, V> rightRight = right.right;
        int rightRightHeight = rightRight != null ? rightRight.height : 0;
        int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;

        int rightDelta = rightLeftHeight - rightRightHeight;
        if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
          rotateLeft(node); // AVL right right
        } else {
          assert (rightDelta == 1);
          rotateRight(right); // AVL right left
          rotateLeft(node);
        }
        if (insert) {
          break; // no further rotations will be necessary
        }

}

When the left node is 2 lower than the right node, save the right left node, the right right node, the right left node height, the right right right node height and the right node height, and calculate the height difference. When the height is equal to 1 or 0, turn left. When the height difference of 1 is right-handed, the root node is left-handed.

When the right node is 2 lower than the left node, the opposite is true.

When the height difference is zero, the node height is modified.

else if (delta == 0) {
        node.height = leftHeight + 1; // leftHeight == rightHeight
        if (insert) {
          break; // the insert caused balance, so rebalancing is done!
        }
}

When the difference between left and right nodes is 1, set the height of left and right nodes to be higher and add one.

Posted by Luvac Zantor on Tue, 23 Jul 2019 08:41:08 -0700