这也正好解释了为什么HashMap底层数组的长度总是 2 的 n 次方。因为这样(数组长度-1)正好相当于一个“低位掩码”。“异或”操作的结果就是散列值的高位全部归零,只保留低位值,用来做数组下标访问。
以初始长度16为例,16-1=15。
2进制表示是00000000 00000000 00001111。
和某hash值做“异或”操作如下,结果就是截取了最低的四位值。 ```
10100101 11000100 00100101 00000000 00000000 00001111 ----------------------------------
00000000 00000000 00000101 //高位全部归零,只保留末四位
更详细的步骤如下: 这里写图片描述
2、获取元素 /**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key. *
*
More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise * it returns {@code null}. (There can be at most one such mapping.) *
*
A return value of {@code null} does not necessarily * indicate that the map contains no mapping for the key; it's also * possible that the map explicitly maps the key to {@code null}. * The {@link #containsKey containsKey} operation may be used to * distinguish these two cases. *
* @see #put(Object, Object) */
public V get(Object key) { Node
return (e = getNode(hash(key), key)) == null ? null : e.value; }
/**
* Implements Map.get and related methods *
* @param hash hash for key * @param key the key
* @return the node, or null if none */
final Node
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k)))) return first;
if ((e = first.next) != null) {
//第一个节点是TreeNode,则采用位桶+红黑树结构, //调用TreeNode.getTreeNode(hash,key), //遍历红黑树,得到节点的value if (first instanceof TreeNode)
return ((TreeNode
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) return e;
} while ((e = e.next) != null); } }
return null; }
树节点的查找:
/**
* Calls find for root node. */
final TreeNode
* Finds the node starting at root p with the given hash and key. * The kc argument caches comparableClassFor(key) upon first use * comparing keys.
*通过hash值的比较,递归的去遍历红黑树,
compareableClassFor(Class k):判断实例k对应的类是否实现了Comparable接口,如果实现了该接口并
在某些时候如果红黑树节点的元素are of the same \C implements
Comparable
*利用他们的compareTo()方法来比较大小,这里需要通过反射机制来check他们到底是不是属于同一个类,是不是具有可比较性.
*/
final TreeNode
int ph, dir; K pk;
TreeNode
else if ((pk = p.key) == k || (k != null && k.equals(pk))) return p; else if (pl == null) p = pr;
else if (pr == null) p = pl;
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, pk)) != 0) p = (dir < 0) ? pl : pr;
else if ((q = pr.find(h, k, kc)) != null) return q; else
p = pl; } while (p != null); return null; }
四、小结
在创建 HashMap 时根据实际需要适当地调整 load factor 的值;如果程序比较关心空间开销、内存比较紧张,可以适当地增加负载因子;如果程序比较关心时间开销,内存比较宽裕则可以适当的减少负载因子。通常情况下,程序员无需改变负载因子的值。
如果开始就知道 HashMap 会保存多个 key-value 对,可以在创建时就使用较大的初始化容量,如果 HashMap 中 Entry 的数量一直不会超过极限容量(capacity * load factor),HashMap 就无需调用 resize() 方法重新分配 table 数组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间(系统需要创建一个长度为 capacity 的 Entry 数组),因此创建 HashMap 时初始化容量设置也需要小心对待。
1.8中的HashMap类代码大约2000多行,此处只挑选了插入、获取元素两个比较重要
的点,先阅读记录一下,后续有时间继续更新。