< 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;//很明显我们这里返回的是 n+1 的值,0000 0000 0000 0000 0000 0000 0000 1111+10000 0000 0000 0000 0000 0000 0001 0000
将它转为十进制,就是 2^4 = 16。我们会发现一个规律,以上的右移运算使得从值为1的最高位开始后所有位的值设置为1 。计算结束后一定再加1,就是1 0000 这样的结构,它一定是 2的n次幂 。因此,这个方法返回的就是大于当前传入值的最小(最接近当前值)的一个2的n次幂的值 。
4、put方法详解
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {// 桶数组Node[] tab;// key、value包装了一个Node对象,用于存储Node p;// i ---> 所处桶的索引// n ---> 桶的大小int n, i;// 桶数组为空,进行resize扩容if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;// 判断如果桶数组该下标为空// 此时该桶中没有任何的结点,新建一个Node放进去if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);// 出现了hash冲突else {// e表示需要覆盖的结点Node e;// k为key值K k;// 第一个Node结点是否与put的Node相同if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;// 当前桶中存放的是红黑树,进行红黑树查找结点else if (p instanceof TreeNode)e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);// 循环查找桶中的链表else {for (int binCount = 0; ; ++binCount) {// 已经到了末尾if ((e = p.next) == null) {// jdk8.0以上都是使用尾插法添加元素p.next = newNode(hash, key, value, null);// 桶中结点数量 > 8, 调用treeifyBin 转化为红黑树或扩容if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}// 判断相同if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}// hash值与key值均相同,将添加的value替换该结点曾经的value// 需要覆盖if (e != null) { // existing mapping for keyV oldValue = http://www.kingceram.com/post/e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;// 判断容量是否需要扩容if (++size> threshold)resize();afterNodeInsertion(evict);return null;}
5、get方法
public V get(Object key) {Node e;return (e = getNode(hash(key), key)) == null ? null : e.value;}// 返回找到的结点,没有找到就返回nullfinal Node getNode(int hash, Object key) {//Entry对象数组Node[] tab;//tab数组中经过散列的第一个位置Node first, e;int n;K k;// 当前桶数组有元素就查找,没有元素直接返回if ((tab = table) != null && (n = tab.length) > 0 &&//first=tab[(n-1)&hash] 找到桶数组中的具体下标,接着在链表或红黑树中查找(first = tab[(n - 1) & hash]) != null) {// 检查第一个Node是否是查找的Nodeif (first.hash == hash &&((k = first.key) == key || (key != null && key.equals(k))))return first;// 查找first后面的Nodeif ((e = first.next) != null) {//如果已经转换为红黑树进行红黑树查找if (first instanceof TreeNode)return ((TreeNode)first).getTreeNode(hash, key);//继续查找链表的下一个结点直到找到的话为空do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}return null;}
6、hash()计算原理
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
这里,会先判断key是否为空,若为空则返回0 。这也说明了是支持key传 null 的 。若非空,则先计算key的值,赋值给h,然后把h右移16位,并与原来的h进行异或处理 。这里就引申出下面几个问题: