Container -- TreeMap, treemap
I. Overview
In the implementation of Map, in addition to our most common HashMap with unordered KEY values, there are also KEY ordered maps, which are commonly used in two categories, one class is a Map of the size ordered by the KEY value, which represents TreeMap, And the other class maintains the insert order of Map, which represents LinkedHashMap. this article introduces TreeMap.
Java provides two sorting interfaces: Comparable and Comparactor:
1. Comparable
Currently, this is a generic interface, and there is only one method compareTo. The classes involved in the comparison need to implement this interface so that the class can be compared. The parameters of this method represent the objects used for comparison. The size relationship between two compare objects is determined based on whether the return value of this interface is> 0, less than 0 or equal to 0.
If a class implements this interface, all objects in the class are comparable, that is, they have the ability to compare. Some of the built-in classes in JDK, such as Integer and Long, are capable of comparison because they implement this interface.
This method is suitable for the classes that compare functions and are one of the features of the class, such as the Integer mentioned above.
2. Comparator
In addition to Comparable, JDK also provides an interface for comparison, which is Comparator. Its interface definition is as follows:
Two methods are defined. The equals method can be understood as Object. the added version of equals is similar to equal. The comparison method is compare, which receives two parameters without any interface, this method compares the sizes of two objects according to certain rules.
Make a comparison. The implementer of this interface is like a referee. Two objects to be compared are used to compare the size. Based on their own rules, it combines the data of the two objects, calculate a value to determine which object is large and small. It is not involved in the comparison.
Sometimes, objects to be compared do not need to be comparable. In some cases, they must be compared to a sequence, or they may be compared according to multiple rules, at this time, it is very suitable to construct a builder using comparator. First, it will not intrude into the original class. In addition, multiple rules are called comparator, which is very convenient.
In JAVA, the following comparison methods are provided for comparison operations, such as set sorting. We can choose based on the situation.
II. Implementation Principle Analysis
The implementation of TreeMap is based on the Tree data structure. The Tree that can be used for sorting in JAVA is the most famous one, and the implementation of TreeMap is based on the red and black trees, to better understand the red and black trees, we have already divided two articles to describe the red and black trees. This article mainly analyzes the construction and use of TreeMap from various aspects.
1. Create
Create a TreeMap in four ways:
We can see that the four methods of TreeMap are mainly divided into two types: one is that comparator is null, the other is not null, as an ordered Map, if comparator is not NULL, it is used for comparison. Otherwise, the KEY should inherit the Comparable interface, which has been explained previously.
As a red/black tree, the entire data storage must be a tree, and the implementation of TreeMap also follows this point. Let's take a look at the construction of the tree.
First, there is a variable that represents the root node.
Next, let's take a look at the definition of a node, which also has the property of a red/black tree node.
In addition to the key and value, the definition of the node also includes the left child, right child, parent node, and node color. Based on these three attributes, we can easily navigate from any node to the upper or lower level. By setting the color of nodes, you can easily implement algorithms related to the red and black trees.
2. Add
Adding a node is the proper place to add a new node to the tree. The first step of this classification is to find the appropriate node. This can be done through the binary tree search algorithm. After the search, there are two results. If the same KEY is found, if the node is not found, you need to associate the node with the original tree. After Association, you have to make another adjustment based on the situation because it may damage the nature of the red and black trees.
The specific algorithm is as follows:
1 public V put(K key, V value) { 2 Entry<K,V> t = root; 3 if (t == null) { 4 compare(key, key); // type (and possibly null) check 5 6 root = new Entry<>(key, value, null); 7 size = 1; 8 modCount++; 9 return null;10 }11 int cmp;12 Entry<K,V> parent;13 // split comparator and comparable paths14 Comparator<? super K> cpr = comparator;15 if (cpr != null) {16 do {17 parent = t;18 cmp = cpr.compare(key, t.key);19 if (cmp < 0)20 t = t.left;21 else if (cmp > 0)22 t = t.right;23 else24 return t.setValue(value);25 } while (t != null);26 }27 else {28 if (key == null)29 throw new NullPointerException();30 Comparable<? super K> k = (Comparable<? super K>) key;31 do {32 parent = t;33 cmp = k.compareTo(t.key);34 if (cmp < 0)35 t = t.left;36 else if (cmp > 0)37 t = t.right;38 else39 return t.setValue(value);40 } while (t != null);41 }42 Entry<K,V> e = new Entry<>(key, value, parent);43 if (cmp < 0)44 parent.left = e;45 else46 parent.right = e;47 fixAfterInsertion(e);48 size++;49 modCount++;50 return null;51 }
The code above is the entire addition process. The key points are highlighted in bold. You can see that when searching, during implementation, the comparison method is determined based on whether the comparator exists. If the corresponding node is not found after comparison, the parent variable records the last node that is not a leaf node, this is the parent node of the new node. After the addition, you need to adjust the tree.
The adjustment algorithm has been introduced in the red/black tree section, so I will not elaborate on it here.
3. Delete
If a node is deleted, the corresponding node is first found through the binary tree search algorithm. If the node has two child nodes, You need to further find the node and perform the delete operation on the node. This ensures that only one node can be deleted.
After the deletion is complete, perform the corresponding Delete and repair operations based on the successor node of the deleted node to ensure that after the deletion, the original tree is still a red/black tree. The complete deletion code is as follows:
private void deleteEntry(Entry<K,V> p) { modCount++; size--; // If strictly internal, copy successor's element to p and then make p // point to successor. if (p.left != null && p.right != null) { Entry<K,V> s = successor(p); p.key = s.key; p.value = s.value; p = s; } // p has 2 children // Start fixup at replacement node, if it exists. Entry<K,V> replacement = (p.left != null ? p.left : p.right); 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; // Null out links so they are OK to use by fixAfterDeletion. p.left = p.right = p.parent = null; // Fix replacement if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parent == null) { // return if we are the only node. root = null; } else { // No children. Use self as phantom replacement and unlink. if (p.color == BLACK) fixAfterDeletion(p); 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; } } }
The repair operation has already been introduced in the previous red/black tree, so it will not be analyzed in detail.
4. Traversal
Compared with a normal Map, as a Sort Map, TreeMap also provides various traversal operations, such as headMap, tailMap, and descendingMap. We can easily traverse in the forward or reverse order, this benefits from bidirectional node Association. I will not describe them here
Iii. Summary
So far, the TreeMap Based on the red and black numbers is completely analyzed. This is a classic implementation. Through the analysis of its source code, we can have a deeper understanding of the red and black trees, you can also learn some good design ideas. At the same time, it also helps us to correctly use the red and black trees.