NOTE

5.24 TreeMap

What TreeMap is, usage, and source analysis of constructors, put, get, containsKey, and remove.

JavaCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. What Is It?

A key-value map implemented using a red-black tree (balanced binary search tree), with O(logN) efficiency.

The iteration order is:

  • the natural order of the keys;
  • or an order defined by a custom Comparator.

2. Usage

public class TreeMapTest
{
    public static void main(String[] args)
    {
        TreeMap<String, String> map = new TreeMap<>();
        map.put("1", "a");
        map.put("3", "c");
        map.put("2", "b");
        map.put("4", "d");

        for (Map.Entry<String, String> entry : map.entrySet())
        {
            /*
            *   1=a
                2=b
                3=c
                4=d
            * */
            System.out.println(entry);
        }
    }
}

3. Source-Code Analysis

3.1. UML

3.2. Constructor

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable//NavigableMap is an ordered-map interface.
{

    // Use a comparator to order keys.
    private final Comparator<? super K> comparator;

    // Root of the red-black tree.
    private transient Entry<K,V> root;

    // Size of the red-black tree.
    private transient int size = 0;

    private transient int modCount = 0;

    // No-argument constructor; uses the natural order of keys by default.
    public TreeMap() {
        comparator = null;
    }

    // Use a custom comparator for ordering.
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
}

3.3. put

public V put(K key, V value) {
    // If root is empty, first construct the red-black tree.
    Entry<K,V> t = root;
    if (t == null) {
        // If there is a custom comparator, use it; otherwise use key.compareTo
        // (therefore the key must implement Comparable).
        compare(key, key); // type (and possibly null) check

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    Entry<K,V> parent;
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;
    // There is a custom comparator.
    if (cpr != null) {
        // Binary-search-tree lookup.
        do {
            parent = t;
            cmp = cpr.compare(key, t.key);
            // Smaller than the current node: go left.
            if (cmp < 0)
                t = t.left;
            // Larger than the current node: go right.
            else if (cmp > 0)
                t = t.right;
            // Found: replace the value.
            else
                return t.setValue(value);
        } while (t != null);
    }
    // No custom comparator.
    else {
        // key must not be null.
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        // Same logic as above.
        do {
            parent = t;
            cmp = k.compareTo(t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
    // Actually insert the node into the tree.
    Entry<K,V> e = new Entry<>(key, value, parent);
    if (cmp < 0)
        parent.left = e;
    else
        parent.right = e;
    // Maintain red-black-tree balance.
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}

3.4. get

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}

final Entry<K,V> getEntry(Object key) {
    // A custom comparator takes this path; the logic is similar to the code below.
    if (comparator != null)
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();
    @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
    // Binary-search-tree lookup, starting at root.
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = k.compareTo(p.key);
        if (cmp < 0)
            p = p.left;
        else if (cmp > 0)
            p = p.right;
        else
            return p;
    }
    return null;
}

3.5. containsKey

public boolean containsKey(Object key) {
    // Simply call getEntry, same as get.
    return getEntry(key) != null;
}

3.6. remove

public V remove(Object key) {
    // Find the node.
    Entry<K,V> p = getEntry(key);
    if (p == null)
        return null;

    V oldValue = p.value;
    // Delete the node.
    deleteEntry(p);
    return oldValue;
}

private void deleteEntry(Entry<K,V> p) {
    modCount++;
    size--;

    // If the node being deleted has both left and right children,
    // find the successor node and make p point to it.
    if (p.left != null && p.right != null) {
        Entry<K,V> s = successor(p);
        p.key = s.key;
        p.value = s.value;
        p = s;
    }

    // Replacement for the node being deleted
    // (use left child if present, otherwise right child).
    Entry<K,V> replacement = (p.left != null ? p.left : p.right);

    // Replace the current node with its left or right child.
    if (replacement != null) {
        // Link replacement to parent
        replacement.parent = p.parent;
        if (p.parent == null)
            root = replacement;
        else if (p == p.parent.left)
            p.parent.left  = replacement;
        else
            p.parent.right = replacement;

        // Clear pointers of the deleted node.
        p.left = p.right = p.parent = null;

        // Rebalance the red-black tree.
        if (p.color == BLACK)
            fixAfterDeletion(replacement);
    // There is only one node in the tree: the node being deleted.
    } else if (p.parent == null) {
        root = null;
    } else {//No left or right child.
        if (p.color == BLACK)
            // Rebalance the red-black tree.
            fixAfterDeletion(p);

        // Modify the parent pointer.
        if (p.parent != null) {
            if (p == p.parent.left)
                p.parent.left = null;
            else if (p == p.parent.right)
                p.parent.right = null;
            p.parent = null;
        }
    }
}

4. References

TreeMap Is This Simple [Source-Code Analysis] - Juejin

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub