[Java Collection source code analysis] TreeMap source code analysis
Reprinted please indicate the source: http://blog.csdn.net/ns_code/article/details/36421085
Preface
This article does not intend to extend the style of the previous articles (add comments to all source code), because we need to understand all the source code of TreeMap. For bloggers, it really takes a lot of time and experience. At present, it seems that there is not much time to invest, so here we want to have a macro grasp of TreeMap by reading the source code, in addition, the implementation of some of these methods is further analyzed.
Introduction to the red/black tree
TreeMap is implemented based on the red and black trees. Here we only give a brief introduction to the red and black trees. The red and black trees are a special binary sorting tree. For more information about the binary sorting tree, see sequence.
The basic properties of the binary sorting tree are as follows:
1. Each node can only 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, the two subnodes are black. That is to say, there cannot be two adjacent red nodes in a path.
5. All paths from any node to each leaf contain the same number of black nodes.
These restrictions make the longest path from any node in the red/black tree to its child leaf node no longer than twice the shortest path. Therefore, it is a near-balanced binary tree.
When it comes to the red and black trees, it is inevitable to compare them with AVL trees. In comparison, AVL trees are strictly balanced binary trees, while red and black trees are not strictly balanced binary trees, but close to balance, it does not make the tree height equal to the number of nodes as in the BST extreme case. In fact, we can use both the red and black trees and AVL trees. However, the red and black trees are widely used, while AVL trees are rarely used. When performing the insert or delete operations, the AVL Tree usually needs to be adjusted more times than the red/black tree (the red/black tree only needs to be rotated three times at most), with relatively low efficiency, the statistical performance of the red/black tree is better than that of the AVL Tree. Of course, the AVL tree may be more efficient in query, but it is actually not much higher.
The insert/delete operation of the red/black tree is simple, that is, the insert/delete operation of the binary sorting tree. The red and black trees are considered abnormal because they are inserted and deleted to adjust the red and black trees (rotation and coloring). There are many situations, due to the limited length and familiarity of bloggers, we do not plan to introduce in detail the various situations and implementations of adjusting the red and black trees after insertion or deletion. We can have a macro understanding, for more information, see Introduction to algorithms or related materials.
TreeMap source code analysis Storage Structure
TreeMap sorting is implemented based on key sorting. Each Entry of TreeMap represents a node of the red/black tree. The data structure of the Entry is as follows:
Static final class Entry <K, V> implements Map. entry <K, V> {// key K key; // value V value; // left child Entry <K, V> left = null; // right child Entry <K, V> right = null; // parent node Entry <K, V> parent; // current node color boolean color = BLACK; // constructor Entry (K key, V value, Entry <K, V> parent) {this. key = key; this. value = value; this. parent = parent ;}...... }
Constructor
Let's take a look at the TreeMap construction method. TreeMap has four constructor methods.
1. Construction Method without Parameters
public TreeMap() { comparator = null; }
No parameter constructor is used, and no comparator is specified. In this case, the implementation of sorting depends on the key. compareTo () method. Therefore, the key must implement the Comparable interface and overwrite the compareTo method.
2. constructor with comparator
public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; }
The constructor with comparator is used. In this case, the sorting depends on the comparator, and the key does not need to implement the Comparable interface.
3. Construction Method with Map
public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); }
The constructor does not specify a comparator. Call the putAll method to add all the elements in the Map to the TreeMap. The source code of putAll is as follows:
// Add all nodes in the map to the public void putAll (Map <? Extends K ,? Extends V> map) {// obtain the map size int mapSize = map. size (); // If the TreeMap size is 0 and the map size is not 0, map is a sorted "key-value Pair" if (size = 0 & mapSize! = 0 & map instanceof SortedMap) {Comparator c = (SortedMap) map ). comparator (); // If the TreeMap and map comparator are equal; // copy all the map elements to the TreeMap and return! If (c = comparator | (c! = Null & c. equals (comparator) {++ modCount; try {buildFromSorted (mapSize, map. entrySet (). iterator (), null, null);} catch (java. io. IOException cannotHappen) {} catch (ClassNotFoundException cannotHappen) {} return ;}// call putAll () in AbstractMap; // putAll () in AbstractMap () it will call the put () super of TreeMap. putAll (map );}
Obviously, if the elements in the Map are sorted, call the buildFromSorted method to copy the elements in the Map, which will be highlighted in the next constructor, if the elements in the Map are not sorted, call the putAll (map) method of AbstractMap. The source code of this method is as follows:
public void putAll(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); }
Obviously, it is used to put (insert) the elements in the Map to the TreeMap, mainly because the elements in the Map are stored unordered, so they need to be inserted to the red and black trees one by one, store them in sequence and meet the needs of the red/black tree.
4. Construction Method with 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) { } }
First, specify the comparator as a comparator of m, depending on whether the constructor is passed in when the m is generated, and then call the buildFromSorted method to insert the elements in SortedMap into the TreeMap, the element division in SortedMap is ordered. In fact, it adds the elements in SortedMap to the TreeMap Based on the TreeMap created by SortedMap.
Insert and delete
The insert operation corresponds to the put method of the TreeMap. In fact, the put operation only needs to follow the insert steps of the binary sorting tree. After the put operation is inserted to the specified position, make adjustments, keep the tree in red and black. Implementation of put source code:
Public V put (K key, V value) {Entry <K, V> t = root; // if the red/black tree is empty, insert the root node if (t = null) {// TBD: // 5045147: (coll) Adding null to an empty TreeSet shold // throw NullPointerException /// compare (key, key ); // type check root = new Entry <K, V> (key, value, null); size = 1; modCount ++; return null;} int cmp; Entry <K, v> parent; // split comparator and comparable paths Comparator <? Super K> cpr = comparator; // locate the insertion position (key, value) in the binary sorting tree. // The Red/black tree is sorted by key, so the key is used for search. If (cpr! = Null) {do {parent = t; cmp = cpr. compare (key, t. key); if (cmp <0) t = t. left; else if (cmp> 0) t = t. right; else return t. setValue (value) ;}while (t! = Null);} else {if (key = null) throw new NullPointerException (); Comparable <? Super K> k = (Comparable <? Super K>) key; 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);} // (key-value) create a node Entry <K, V> e = new Entry <K, V> (key, value, parent ); if (cmp <0) parent. left = e; else parent. right = e; // after a new node is inserted, call fixAfterInsertion to adjust the red/black tree. FixAfterInsertion (e); size ++; modCount ++; return null ;}
Here fixAfterInsertion is the method to adjust the tree after the node is inserted, which is not described here.
The delete operation and the deleteEntry method of the corresponding TreeMap can also be implemented by following the operation steps of the binary sorting tree. After deleting the specified node, adjust the tree. The source code of the deleteEntry method is as follows:
// Delete the "p" private void deleteEntry (Entry <K, V> p) {modCount ++; size --; if (p. left! = Null & p. right! = Null) {Entry <K, V> s = successor (p); p. key = s. key; p. value = s. value; p = s;} Entry <K, V> replacement = (p. left! = Null? P. left: p. right); if (replacement! = Null) {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; p. left = p. right = p. parent = null; if (p. color = BLACK) fixAfterDeletion (replacement);} else if (p. parent = null) {root = null;} else {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 following fixAfterDeletion method is used to adjust the tree after the node is deleted.
Many other methods are not described here.
Summary
In this article, the analysis of TreeMap is a little bit easier than in the previous articles. TreeMap uses less HashMap than HashMap. We can compare it with me at a macro level.
1. TreeMap is sorted by key. Its sorting and positioning depend on the comparator or overwrite the Comparable interface. Therefore, the hashCode and equals methods do not need to be overwritten by the key, duplicate keys can be excluded. The HashMap key must overwrite the hashCode method and equals method to ensure that no duplicate keys exist.
2. TreeMap's query, insertion, and deletion efficiency are not as high as HashMap. Generally, TreeMap is used only when key sorting is required.
3. The key of TreeMap cannot be null, while the key of HashMap can be null.
Note: the source code of TreeSet and HashSet will not be analyzed. The two are implemented based on TreeMap and HashMap respectively, but the corresponding nodes only have keys but no values, therefore, if you have a better understanding of TreeMap and HashMap, it is very easy to understand TreeSet and HashSet.