1. Brief description
- Today, let's introduce TreeMap. TreeMap is a Map based on red black tree structure.
- What is red and black number?
- (1) Each node must be red or black;
- (2) The root node is black;
- (3) Each leaf node (NIL node, empty node) is black;
- (4) If a node is red, its two byte points are black, that is, two adjacent red nodes cannot appear on a path;
- (5) All paths from any node to each leaf contain the same number of black nodes.
- How to maintain the characteristics of red black tree?
- Every time a node is added or deleted, the red black tree changes and may no longer meet the above five characteristics. Therefore, in order to maintain the above characteristics of the red black tree, three actions are required: left rotation, right rotation and coloring.
2. Sum up
- It inherits AbstractMap and implements the NavigableMap interface, so it supports a series of navigation methods and implements Cloneable and Serializable interfaces, so it supports replication (copy) and serialization.
- You can pass in your own comparator in the constructor.
- Its bottom layer is implemented based on red black tree, which is disordered and non repeatable. Null keys are not allowed, but null values are allowed.
- Single thread is safe, multi thread is not safe.
3. Analysis
3.1 interface
- Before we analyze the source code of TRE Map.
public interface Map<K, V> {
...
// increase
V put(K key, V value);
// Delete
V remove(Object key);
// check
V get(Object key);
...
}
- In the above interface, I only extracted several important methods, and then take this as the subsequent key analysis goal. The source code corresponding to its Map interface is far more than the above methods. Interested students can read it by themselves.
3.2. Static internal class
public class TreeMap<K, V> extends AbstractMap<K, V>
implements NavigableMap<K, V>, Cloneable, java.io.Serializable {
...
static final class TreeMapEntry<K, V> implements Map.Entry<K, V> {
K key;
V value;
// Left node
TreeMapEntry<K, V> left;
// Right node
TreeMapEntry<K, V> right;
// Parent node
TreeMapEntry<K, V> parent;
// Color of each node, red black tree feature, red or black
boolean color = BLACK;
/**
* Parameterized constructor
*
* @param key key
* @param value Element value
* @param parent Parent node
*/
TreeMapEntry(K key, V value, TreeMapEntry<K, V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
...
}
...
}
3.3. Member variables
public class TreeMap<K, V> extends AbstractMap<K, V>
implements NavigableMap<K, V>, Cloneable, java.io.Serializable {
// This is a comparator to facilitate operations such as inserting and finding elements
private final Comparator<? super K> comparator;
// Root node of red black tree: each node is a treemapeentry
private transient TreeMapEntry<K, V> root;
// Number of elements in red black tree
private transient int size = 0;
// Number of modifications to red black tree structure
private transient int modCount = 0;
...
}
3.4 constructor
public class TreeMap<K, V> extends AbstractMap<K, V>
implements NavigableMap<K, V>, Cloneable, java.io.Serializable {
...
/**
* non-parameter constructor
* By default, and the comparator is empty
*/
public TreeMap() {
comparator = null;
}
/**
* Parameterized constructor
*
* @param comparator Specify a comparator
*/
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
/**
* Parameterized constructor
*
* @param m Specify a map creation, the comparator is empty, and the elements are sorted naturally
*/
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
/**
* Parameterized constructor
*
* @param m Specify SortedMap and maintain the order of TreeMap according to the comparator of SortedMap
*/
public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}
...
}
3.5. Increase operation
public class TreeMap<K, V> extends AbstractMap<K, V>
implements NavigableMap<K, V>, Cloneable, java.io.Serializable {
...
/**
* @param key
* @param value
* @return
*/
public V put(K key, V value) {
TreeMapEntry<K, V> t = root;
// If root is null, it means that the first element is added, and a treemapeentry is directly instantiated and assigned to root
if (t == null) {
// Type (possibly empty) check
compare(key, key);
root = new TreeMapEntry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
// If root is not null, the element already exists
TreeMapEntry<K, V> parent;
// Split comparator and comparable path
Comparator<? super K> cpr = comparator;
if (cpr != null) {// If the comparator is not null, the comparator is used
do {// Find the insertion location of the element
parent = t;// parent assignment
cmp = cpr.compare(key, t.key);
if (cmp < 0)// If the current key is less than the node key, search the left subtree
t = t.left;
else if (cmp > 0)// The current key is greater than the node key. Search the subtree to the right
t = t.right;
else// Update the node value directly in case of equality
return t.setValue(value);
} while (t != null);
} else {// If the comparator is null, the default comparator is used
if (key == null)// If the key is null, an exception is thrown
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {// Find the insertion location of the element
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);
}
// Define a new node
TreeMapEntry<K, V> e = new TreeMapEntry<>(key, value, parent);
// Decide whether to insert into the left subtree or the right subtree according to the comparison results
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);// Maintain the nature of red black tree and correct it after insertion
size++;// Self increment of element tree
modCount++;// The element tree structure changes and increases automatically
return null;
}
/**
* Maintain the red black tree property after inserting elements (red black tree correction after insertion)
*
* @param x Inserted node
*/
private void fixAfterInsertion(TreeMapEntry<K, V> x) {
// Set the color of the newly inserted node to red
x.color = RED;
// The while loop ensures that the newly inserted node x is not the root node or the parent node of the newly inserted node x is not red (no adjustment is required in these two cases)
while (x != null && x != root && x.parent.color == RED) {
// If the parent of the newly inserted node x is the left child of the grandfather node
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
// Gets the uncle node of the newly inserted node x
TreeMapEntry<K, V> y = rightOf(parentOf(parentOf(x)));
// If the parent node of the newly inserted x is red
if (colorOf(y) == RED) {
// Set the parent node of x to black
setColor(parentOf(x), BLACK);
// Set the uncle node of x to black
setColor(y, BLACK);
// Set the grandfather node of x to red
setColor(parentOf(parentOf(x)), RED);
// Point x to the grandfather node. If the parent node of the grandfather node of X is red, continue the cycle according to the above steps
x = parentOf(parentOf(x));
} else {
// If the uncle node of the newly inserted x is black or missing, and the parent node of X is the right child of the grandfather node
if (x == rightOf(parentOf(x))) {
// Left handed parent node
x = parentOf(x);
rotateLeft(x);
}
// If the uncle node of the newly inserted x is black or missing, and the parent node of X is the left child of the grandfather node
// Set the parent node of x to black
setColor(parentOf(x), BLACK);
// Set grandfather's node to red
setColor(parentOf(parentOf(x)), RED);
// Grandfather node of dextral x
rotateRight(parentOf(parentOf(x)));
}
} else { // If the parent node of the newly inserted node x is the right child of the grandfather node, it is similar to the one above
TreeMapEntry<K, V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
// Finally, set the root node to black
root.color = BLACK;
}
...
}
3.6. Delete
public class TreeMap<K, V> extends AbstractMap<K, V>
implements NavigableMap<K, V>, Cloneable, java.io.Serializable {
...
/**
* Delete according to the key
*
* @param key key
* @return Returns the element value after deletion
*/
public V remove(Object key) {
// Find the corresponding node object according to the key
TreeMapEntry<K, V> p = getEntry(key);
if (p == null)
return null;
// Record the value corresponding to the key for return
V oldValue = p.value;
// Delete node
deleteEntry(p);
return oldValue;
}
/**
* Delete node
*
* @param p node
*/
private void deleteEntry(TreeMapEntry<K, V> p) {
modCount++;
// Number of elements minus one
size--;
// If the left child and right child of the deleted node p are not empty, find its replacement section
if (p.left != null && p.right != null) {
// Find alternate nodes for p
TreeMapEntry<K, V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
}
TreeMapEntry<K, V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) {
// Copy the parent node of p to the substitute node
replacement.parent = p.parent;
// If the parent node of the substitute node p is empty, that is, p is the following node, set the replacement as the root node
if (p.parent == null)
root = replacement;
// If the substitute node p is the left child of its parent node, set replacement to the left child of its parent node
else if (p == p.parent.left)
p.parent.left = replacement;
// If the substitute node p is the left child of its parent node, set replacement to the right child of its parent node
else
p.parent.right = replacement;
// Point the pointers of the left, right and parent of the substitute node p to null
p.left = p.right = p.parent = null;
// If the color of the substitute node p is black, you need to adjust the red black tree to maintain its balance
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) {
// If the replacement node p has no parent node, it means that p is the root node and can be deleted directly
root = null;
} else {
// If the color of p is black, adjust the red black tree
if (p.color == BLACK)
fixAfterDeletion(p);
// Next, delete the replacement node p
if (p.parent != null) {
// Dereference p from its parent node
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
// Dereference p to the parent node of P
p.parent = null;
}
}
}
...
}
3.7 check operation
public class TreeMap<K, V> extends AbstractMap<K, V>
implements NavigableMap<K, V>, Cloneable, java.io.Serializable {
...
/**
* Query according to key
*
* @param key key
* @return Returns the element value after searching. If it does not exist, it returns null
*/
public V get(Object key) {
TreeMapEntry<K, V> p = getEntry(key);
return (p == null ? null : p.value);
}
/**
* Get the corresponding node according to the key
*
* @param key key
* @return Return node
*/
final TreeMapEntry<K, V> getEntry(Object key) {
// If the comparator is empty, just use the key as the comparator for query
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
// Get root node
TreeMapEntry<K, V> p = root;
// Here comes the core: start from the root node and judge whether it is in the left subtree or the right subtree according to the comparator
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;
}
...
}