JAVA collection of the two -TreeMap

Hello everyone, today we learn about another family member Map: TreeMap.

First, the basic concept

Before introducing TreeMap, we come to understand a data structure: a binary tree . I believe the students learned to know the data structure, data is stored in the form of this structure when looking for efficiency is very high.

A schematic view of a binary tree structure (from Baidu Encyclopedia)

Binary tree (from Baidu Encyclopedia)

 

Binary search tree structure in turn subdivided into two forks balanced tree fork

Binary search tree

 

Binary search tree is an ordered tree, all left the child 's value is less than the value of the leaf node value of the value of all the right child 's value is greater than the value of the leaf nodes of. The advantage of this is: If you need to find the key data elements in accordance with, as long as the value of the comparison value to the current node (node value less than the current value, go to the left, or turn right to go), this way, every time you can reduce by half the operation, so more efficient.

Search tree is a binary further than binary tree balanced , balanced binary tree in addition to ensuring ordered, but also can be maintained around the height of each node in the subtree differ by no more than 1. Common AVL balanced tree has a tree, Treap, red-black tree , splay trees, and so on.
A red-black tree to increase the storage node represented by bit at each node color , or may be RED BLACK. By way of limitation on the coloring of any one path from the root to each leaf node, red-black trees ensure that no path is deeper than twice the other paths , and thus is close to the equilibrium.

In TreeMap, that is the use of red-black tree . (End of the previous article, we mentioned when HashMap list is too long in an array slot, the list will be put into a red-black tree to store data)

Second, a constructor 

First look at the constructor TreeMap. A total of four TreeMap constructor.

1, the constructor with no arguments

 public TreeMap() {comparator = null;}

Using the constructor with no arguments, the comparator is not specified, this time, to achieve sort depends key.compareTo () method, and therefore the Comparable interface must implement the key, and wherein the override compareTo method.

2, with a comparator constructor

public TreeMap(Comparator<? super K> comparator) {

this.comparator = comparator;

}
 Using the constructor that takes a comparator, at this time, dependent on the sort of the comparator, key Comparable interface can not implement.

3, the construction method with Map

public TreeMap(Map<? extends K, ? extends V> m) {

comparator = null;

putAll(m);

}
This configuration also does not specify the method of the comparator, the method calls putAll Map all the elements was added to a TreeMap. putAll source as follows:
// 将map中的全部节点添加到TreeMap中

public void putAll(Map<? extends K, ? extends V> map) {

// 获取map的大小

int mapSize = map.size();

// 如果TreeMap的大小是0,且map的大小不是0,且map是已排序的“key-value对”

if (size==0 && mapSize!=0 && map instanceof SortedMap) {

Comparator c = ((SortedMap)map).comparator();

// 如果TreeMap和map的比较器相等;

// 则将map的元素全部拷贝到TreeMap中,然后返回!

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;

}

}

// 调用AbstractMap中的putAll();

// AbstractMap中的putAll()又会调用到TreeMap的put()

super.putAll(map);

}
Obviously, if the Map, the elements are sorted, it calls buildFromSorted method to copy elements of the Map, which is a constructor in the next focus will be mentioned, but if the Map elements are not sorted, it calls AbstractMap the putAll (map), the method source code 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 a Map of elements one by one put (inserted) into the TreeMap, mainly because of the Map elements are stored unordered, so to insertion into a red-black tree, so orderly storage, and meet the nature of the red-black tree.

4, the 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 comparator If m comparator, depending on whether the incoming call the constructor constructor specified when generating m, then call buildFromSorted method, the elements into SortedMap TreeMap, since the division elements in SortedMap orderly, in fact it is based on TreeMap SortedMap create, add SortedMap corresponding element in the TreeMap.

Third, the basic principles of internal storage 

private final Comparator<? super K> comparator;

private transient Entry<K,V> root;

private transient int modCount = 0;

//静态成员内部类 从源码中摘取部分代码,能说明内部结构即可

static final class Entry<K,V> implements Map.Entry<K,V> {

// 键

K key;

// 值

V value;

// 左孩子

Entry<K,V> left = null;

// 右孩子

Entry<K,V> right = null;

// 父节点

Entry<K,V> parent;

// 当前节点颜色

boolean color = BLACK;


// 构造函数

Entry(K key, V value, Entry<K,V> parent) {

this.key = key;

this.value = value;

this.parent = parent;

}

}

From the code, we can easily see, the internal comparator comprises a comparator (or Key value is set to the comparator is set to incoming external or comparator), root root (red and black points the root of the tree), to modify the number of records ModCount (to check for structural set), there is a static inner class (in fact, be understood as a tree node), which store keys and key values and a value, further pointing left child and right child of the "Guidelines", as well as pointing to the parent node of the "pointer" and, finally, a sign  Color . That is to say, pointing to a root root of the tree, and this in turn is linked to the root of a tree, and finally through the root can traverse the entire tree.

Four, put add elements to the collection 

In the understanding of the internal structure of TreeMap, we can see how he would hang a whole grain element node to the tree.

public V put(K key, V value) {

Entry<K,V> t = root;

if (t == null) {

// 若红黑树为空,则插入根节点

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;

// 找出(key, value)在二叉排序树中的插入位置。

// 红黑树是以key来进行排序的,所以这里以key来进行查找。

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();

@SuppressWarnings("unchecked")

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)新建节点

Entry<K,V> e = new Entry<>(key, value, parent);

if (cmp < 0)

parent.left = e;

else

parent.right = e;

// 插入新的节点后,调用fixAfterInsertion调整红黑树。

fixAfterInsertion(e);

size++;

modCount++;

return null;

}
First determines whether the root node is empty, empty if it is directly create a parent node and assign null, which is the root node of the tree, or null skip the rest of the code. If the node is not empty with, go comparator determines whether null (the determination value is the default key of the comparator is a comparator or external incoming comparator), if the value of an external incoming comparator, by comparing the cycle location node value calculation key is to be added (the process if there is equal to the key value of a node, and the value of key to be added, indicating that a modification operation to modify its value value return the old value value). 

If you do not pass the comparator from the outside when creating the object, first determine whether the value of the key is null (if it is thrown null pointer exception), it was said: Why should we make a judgment on whether the key is empty it? The above is not the judge did not do it? The answer is: if an external comparator is passed, then no problem, but if it is the default key comparator, that even if the key is null call parity is inevitable short-selling pointer exception. The next thing to do and the same as above. 

Program execution to the end, and we know that it is important to: parent points to the parent node node node is the last we are going to add. Finally, create a node based on key and value and parent, then determined in accordance with the above judgment of this node is the left child or right child. 

This method has a  fixAfterInsertion (E) ; call this function we just created can be done by moving the tree after the re-constructed red-black tree .

To sum up the whole implementation process put method:

  1. Determining whether the tree is empty, the operation is very simple empty tree
  2. Determine the source of the comparator make different operations (comparison value to determine the position value)
  3. Build a new tree node hang
  4. Red-black tree reconstruction method call

Among them, we have to distinguish between that is why sometimes return null, and sometimes returns the value of the old nodes, the main difference lies, put methods as additive elements and modify elements of the two functions, add an element of unity when returns null, modify the elements of unity when the returned value is the element before other changes.

  V. Remove node element according to the value of the key 

Once you know the implementation of a put operation, we will look to achieve delete operations. First look at the remove method:

public V remove(Object key) {

Entry<K,V> p = getEntry(key);

if (p == null)

return null;


V oldValue = p.value;

deleteEntry(p);

return oldValue;

}
Delete operation is mainly a combination of the two operations, one gets the specified elements getEntry (key), is to remove a specified element deleteEntry (p). We look at how to get the specified element.

final Entry<K,V> getEntry(Object key) {

// Offload comparator-based version for sake of performance

if (comparator != null)

return getEntryUsingComparator(key);

if (key == null)

throw new NullPointerException();

@SuppressWarnings("unchecked")

Comparable<? super K> k = (Comparable<? super K>) key;

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;

}

This code is easy to understand, still depending on the source of the comparator two cases (due to similar treatment in both cases, one specifically referred to herein), p points to the root root node, loop through Comparison if the key and the key to the current cycle is equal, not equal according to the size of the left or right, if equal execution return p; return to this node. If after the completion of traversing the entire tree, specified nodes did not find the key value is returned null if the node is not found. This is the search method.

Here we look at ways to delete the specified node.

Before we look at the code first look at the whole idea, to be deleted node may have the following three conditions:

  • The node is a leaf node, ie no left child and right child
  • The node has only one child node
  • The node has two children nodes

The first case, the node is directly deleted, and the corresponding parent node assigned to a reference null 

The second case, the node will skip its parent node points to the child node

Scenario 2

The third case, the node to be deleted to find alternative successor node will successor node to the node to be deleted and delete the successor node (the second case and remove the same way)

Three cases

 

Let's look at the code:

/*代码虽多,我们一点一点看*/

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;

}

}

}

First, determine whether to be deleted node has two children, and if so, call the function returns the successor successor node, the node to be deleted is larger than the smallest of the node

Source as follows:


static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {

if (t == null)

return null;

else if (t.right != null) {

Entry<K,V> p = t.right;

while (p.left != null)

p = p.left;

return p;

} else {

Entry<K,V> p = t.parent;

Entry<K,V> ch = t;

while (p != null && ch == p.right) {

ch = p;

p = p.parent;

}

return p;

}

}

For this statement: Entry> K, V <replacement = (p.left = null p.left: p.right!?);, We the above three cases replacement values ​​worthy of study, if it is the first where (leaf node), then the replacement value is null, into the underlying judgment, if the first through the second to be deleted is determined whether the node is the root (parent node of the root node only be null) If only one is a whole tree node, then delete it directly, if not root on the note is a leaf node, then the parent node assigned to null and then delete it. 

For the second case (only one child node time), the top if the statement is not, if that node is a replacement for the left child node, then skip this node parent node linked in the following node to be deleted, if that child is a right child, Replacement for the node, in the same manner. 

The third case (to be deleted node has two child nodes), it is certainly the most execution if the above statement in the code, find the successor node to replace the node to be deleted (successor node must not have left children), successful the conversion issues for deletion successor node, and because certain successor node left no children, the whole issue has been converted to the above two cases, (if no successor node is the right child first, if there is a second ) so replacement = p.right, the following process points. The end of the deletion method. 

This paper analyzes kick the tires on a TreeMap, HashMap TreeMap with not so much, we have a grasp on the macro and can compare.

 1, TreeMap is sorted by key, its sorting and positioning relies comparator or override Comparable interface, and therefore does not need to override the hashCode method and the equals key methods, can be ruled out duplicate key, and the key HashMap We need to make sure there are no duplicate key by overwriting hashCode method and the equals method.

2, TreeMap query, insert, delete efficiency are not high HashMap, generally only to be sort of the key when using TreeMap.

   3, TreeMap the key can not be null , and the HashMap key can be null.

   Note: The source of TreeSet and HashSet not analyze, and both are TreeMap HashMap based implementation, only the corresponding node only key, and no value, and therefore TreeMap HashMap better understanding of words, and of the HashSet TreeSet it will be very easy to understand.

Published 36 original articles · won praise 161 · views 480 000 +

Guess you like

Origin blog.csdn.net/weixin_42476601/article/details/104392682