标签:获取 span png equal 直接插入 assert imu ppi 查找
关于扩容,在resize()方法中,一般是扩容一倍 newCap = oldCap << 1. 扩容的同时,若原table中存在元素,则需要将原table中的元素进行重新计算哈希桶位置.
在设置初始值的时候,需要将容器大小设置为最接近2次幂的值,例如new HashMap<>(5);则初始容器大小为8.源码为:
1 //Returns a power of two size for the given target capacity. 2 static final int tableSizeFor(int cap) { 3 int n = cap - 1; 4 n |= n >>> 1; 5 n |= n >>> 2; 6 n |= n >>> 4; 7 n |= n >>> 8; 8 n |= n >>> 16; 9 return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; 10 }
为什么HashMap的容量一定要设置为2次幂呢? 方便put和get. 一般获取位置使用取余运算%,h % length即可得到哈希值所在的位置,而HashMap中由于容量为2次幂,它使用的是
h & (length - 1),取余运算m % n可以等效于m - m / n * n,位运算比取余运算快了不少.在jdk1.7中,存在方法indexFor用于计算哈希值的位置
1 /** 2 * Returns index for hash code h. 3 */ 4 static int indexFor(int h, int length) { 5 // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; 6 return h & (length-1); 7 }
而在jdk1.8中,该方法已经不存在了,直接使用在各个方法中,例如 putVal中 p = tab[i = (n - 1) & hash]
关于哈希冲突,由于 h & (n - 1) 会得到同一个位置的值,就会造成冲突.当冲突小时,HashMap会将该桶转换成一个链表,每个桶的元素为:
1 static class Node<K,V> implements Map.Entry<K,V> { 2 final int hash; 3 final K key; 4 V value; 5 Node<K,V> next; 6 //省略其他代码 7 }
可以看出当next没值时,就没有哈希冲突,当next有值时,该桶即为一个链表结构.当冲突高于某个界限(这个界限为TREEIFY_THRESHOLD = 8)时,该桶将转变为一颗红黑树. 为什么要转换成红黑树呢? 当存在哈希冲突时,对链表的查找复杂度为 O(N),是线性的,当冲突较大时,查找会较慢,此时转换成红黑树,红黑树的查找复杂度为O(logN),比链表快.源代码如下:
1 //是否冲突,不冲突直接插入到该桶 2 if ((p = tab[i = (n - 1) & hash]) == null) 3 tab[i] = newNode(hash, key, value, null); 4 else { 5 //存在冲突 6 Node<K,V> e; K k; 7 if (p.hash == hash && 8 ((k = p.key) == key || (key != null && key.equals(k)))) 9 e = p; //key的hash相等以及值也相等,则覆盖 10 else if (p instanceof TreeNode) 11 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //已经是颗红黑树,则往树里插入元素 12 else { //链表 13 for (int binCount = 0; ; ++binCount) { 14 if ((e = p.next) == null) { 15 //将桶转变成链表 16 p.next = newNode(hash, key, value, null); 17 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 18 //大于界限,转换成红黑树 19 treeifyBin(tab, hash); 20 break; 21 } 22 if (e.hash == hash && 23 ((k = e.key) == key || (key != null && key.equals(k)))) 24 break; 25 p = e; 26 } 27 } 28 if (e != null) { // existing mapping for key 29 V oldValue = e.value; 30 if (!onlyIfAbsent || oldValue == null) 31 e.value = value; 32 afterNodeAccess(e); 33 return oldValue; 34 } 35 }
哈希冲突时很常见的
当存在不规范的代码,即对象hashcode相等当时equals不相等时,该对象当key必存在冲突.
1 Map<String,String> map = new HashMap<>(); 2 map.put("a","a"); 3 map.put("b","a"); 4 map.put("c","a"); 5 map.put("d","a"); 6 map.put("e","a"); 7 map.put("f","a"); 8 map.put("g","a"); 9 map.put("r","a"); 10 map.put("s","a"); 11 map.put("t","a"); 12 map.put("w","a"); 13 map.put("x","a"); 14 map.put("y","a"); 15 map.put("z","a");
在上面这段代码中,跟踪程序,当执行到14行时,由于存在了13个元素,大于16*0.75,此时扩容,跟踪HashMap中的table
扩容后重新计算元素的哈希桶位置后
原先为9个桶13个元素,重新计算后得到13个桶13个元素,可以判断出此时已经存在哈希冲突. 进一步跟踪,可以得到冲突的桶:
b -> a.next = r -> a c -> a.next = s -> a d -> a.next = t -> a g -> a.next = w -> a
标签:获取 span png equal 直接插入 assert imu ppi 查找
原文地址:http://www.cnblogs.com/joels/p/7646795.html