Java8 in HashMap is necessary to look at the next to explore the

Java8 in HashMap is necessary to look at the next to explore the


72f2b032088f447d938ac6bc87e2d423



HashMap is among Java developers use very much of a data structure, because it can quickly navigate to the need to find data, its fastest speed can reach O (1), the worst of times can achieve O (n) . In this paper, the analysis Java8 HashMap as a prototype, because different versions of the JDK HashMap, there may not be the same as on the underlying implementation.

HashMap all the data storage array, each element of the stored subscript of an array, and the operation is done by subtracting a length of the array in accordance with the Hash value of the key storage element, as follows:

index = (length_of_array - 1) & hash_of_the_key;


Data stored in the array elements and Node TreeNode structure uses two data structures, when the Hash value corresponding to a single memory element is less than 8, the default value is stored in Node-way linked list, when a single element is greater than the value stored Hash 8 when one which uses data stored in the TreeNode structure.

Because the eight time corresponding to a single element Hash value or less, which is the worst query time O (8), but when the Hash value of a single element corresponding to more than eight, and then proceed through the Node query way linked list, it will become the slower; the common node at this time HashMap will be converted to a TreeNode node (red-black tree) is stored, which is due to TreeNode about twice the size of the space occupied by conventional node, but its query speed can be guaranteed, this is through space for time. When the elements included in the TreeNode becomes relatively small, in order to take up storage space, will be converted to the node Node-way linked list of ways, they can be converted between each other.

Node:

static class Node<K,V> implements Map.Entry<K,V> {
 final int hash;
 final K key;
 V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) {
 this.hash = hash;
 this.key = key;
 this.value = value;
 this.next = next;
 }
 ......
}


Node can be seen that each includes four attributes, namely:

hash values: the current Node Hash value 
key: the current Node keyValue: Node's current valuenext: represents a Node pointer points to the next, the same hash value Node, traversed by the next lookup


TreeNode:

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; // red-black tree links
 TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; // needed to unlink next upon deletion
 boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next);
 }
 ......
}


TreeNode can be seen using a red-black tree (Red Black Tree) data structure, red-black tree is a self-balancing binary search tree, keeping a balanced binary search tree by a specific operation during insert and delete operations, so as to obtain higher lookup performance, even in the worst case running time is also very good, and in practice is very efficient, it can do to find in O (log n) time, and other insertions and deletions, where n is the number of elements in the tree.

The following is a schematic diagram on HashMap storage structure:


5c32e4bb4c15444ea6fdf263f49484e1



Write data (all are in comments)

, As follows:

//写入数据public V put(K key, V value) { //首先根据hash方法,获取对应key的hash值,计算方法见后面
 return putVal(hash(key), key, value, false, true);
}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
 Node<K,V>[] tab; Node<K,V> p; int n, i; //判断用户存放元素的数组是否为空
 if ((tab = table) == null || (n = tab.length) == 0) //为空则进行初使化,并将初使化后的数组赋值给变量tab,数组的长值赋值给变量n
 n = (tab = resize()).length; //判断根据hash值与数组长度减1求与得到的下标,
 //从数组中获取元素并将其赋值给变量p(后续该变量p可以继续使用),并判断该元素是否存在
 if ((p = tab[i = (n - 1) & hash]) == null) //如果不存在则创建一个新的节点,并将其放到数组对应的下标中
 tab[i] = newNode(hash, key, value, null); else {//根据数组的下标取到了元素,并且该元素p且不为空,下面要判断p元素的类型是Node还是TreeNode
 Node<K,V> e; K k; //判断该数组对应下标取到的第一值是不是与正在存入值的hash值相同、
 //key相等(可能是对象,也可能是字符串),如果相等,则将取第一个值赋值给变量e
 if (p.hash == hash &&
 ((k = p.key) == key || (key != null && key.equals(k))))
 e = p; //判断取的对象是不是TreeNode,如果是则执行TreeNode的put方法
 else if (p instanceof TreeNode)
 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else {//是普通的Node节点,
 //根据next属性对元素p执行单向链表的遍历
 for (int binCount = 0; ; ++binCount) { //如果被遍历的元素最后的next为空,表示后面没有节点了,则将新节点与当前节点的next属性建立关系
 if ((e = p.next) == null) { //做为当前节点的后面的一个节点
 p.next = newNode(hash, key, value, null); //判断当前节点的单向链接的数量(8个)是不是已经达到了需要将其转换为TreeNode了
 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
 //如果是则将当前数组下标对应的元素转换为TreeNode
 treeifyBin(tab, hash); break;
 } //判断待插入的元素的hash值与key是否与单向链表中的某个元素的hash值与key是相同的,如果是则退出
 if (e.hash == hash &&
 ((k = e.key) == key || (key != null && key.equals(k)))) break;
 p = e;
 }
 } //判断是否找到了与待插入元素的hash值与key值都相同的元素
 if (e != null) { // existing mapping for key
 V oldValue = e.value; //判断是否要将旧值替换为新值
 if (!onlyIfAbsent || oldValue == null) //满足于未指定不替换或旧值为空的情况,执行将旧值替换为新值
 e.value = value;
 afterNodeAccess(e); return oldValue;
 }
 }
 ++modCount; if (++size > threshold)
 resize();
 afterNodeInsertion(evict); return null;
}


Hash值的计算方法:

// 计算指定key的hash值,原理是将key的hash code与hash code无符号向右移16位的值,执行异或运算。// 在Java中整型为4个字节32位,无符号向右移16位,表示将高16位移到低16位上,然后再执行异或运行,也// 就是将hash code的高16位与低16位进行异或运行。// 小于等于65535的数,其高16位全部都为0,因而将小于等于65535的值向右无符号移16位,则该数就变成了// 32位都是0,由于任何数与0进行异或都等于本身,因而hash code小于等于65535的key,其得到的hash值// 就等于其本身的hash code。static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}


计算逻辑如下图所示:


590f3683c9744d349e29629a57c61b8b




读取数据(一切皆在注释中)

 public V get(Object key) {
 Node<K,V> e; //根据Key获取元素
 if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder)
 afterNodeAccess(e); return e.value;
 } final Node<K,V> getNode(int hash, Object key) {
 Node<K,V>[] tab; Node<K,V> first, e; int n; K k; //if语句的第一个判断条件
 if ((tab = table) != null //将数组赋值给变量tab,将判断是否为null
 && (n = tab.length) > 0 //将数组的长值赋值给变量n
 && (first = tab[(n - 1) & hash]) != null) {//判断根据hash和数组长度减1的与运算,计算出来的的数组下标的第一个元素是不是为空
 //判断第一个元素是否要找的元素,大部份情况下只要hash值太集中,或者元素不是很多,第一个元素往往都是需要的最终元素
 if (first.hash == hash && // always check first node
 ((k = first.key) == key || (key != null && key.equals(k)))) //第一个元素就是要找的元素,因为hash值和key都相等,直接返回
 return first; if (! (e = first.next) = null) {// If the element is not the first to find the element, it is determined whether there are elements next point
 //有元素,判断其是否是TreeNode
 if (first instanceof TreeNode) // The data is acquired is TreeNode TreeNode way 
 return ((TreeNode <K, V>) First) .getTreeNode (the hash, Key); 
 do {// Node is a singly linked list, next through cycle match, find exit, or until you exit the match was the last element 
 IF (e.hash == hash && 
 ((k = e.key) || == Key (Key! = null && key.equals (k) ))) return E; 
 !} the while ((E = e.next) = null); 
 } 
 } // returns null if not found 
 return null; 
 }

Like this article, you can point to the author likes to point at attention, will share Java-related articles every day!

Remember to focus on me oh, will occasionally presented benefits, including consolidation of interview questions, learning materials, source code, etc. ~ ~


Guess you like

Origin blog.51cto.com/14456091/2432756