JDK1.7-
数组+链表
1.HashMap.put 返回值是原本的value, 如果当前的key是第一次插入,则返回的是null
2.HashMap 插入在链表的时候,是头插入法。Node.next=arr[index] arr[index]=Node;
2.1 头插入会对每次扩容的时候,起作用。扩容的数组的链表和旧数组的链表的顺序是相反的。
3.注意:
在 put操作的时候,如果先要判断key是否存在,这里要进行遍历,
如果key存在,就把当前的key对应的value用当前key对应的value覆盖了。
如果key不存在,还是插在链表的尾部,因为只有把链表全部遍历完,才能知道key是否存在。
4.如何找出一个整数离它最近的比它大的2的幂次次数
number=10;
(number-1)<<1
9 0000 1001
18 0001 0010
Integer.highestOneBit((number-1)<<1);
public static int highestOneBit(int i) {
// HD, Figure 3-1
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
1+2+4+8+16=31 int数据类型 最多31位 因为最高位保存符号位
18 0001 0010 18
>>1 0000 1001 9
| 0001 1011 27
>>2 0000 0110 6
| 0001 1111 31
>>4 0000 0001 1
| 0001 1111 31
>>8 0000 0000 0
| 0001 1111 31
>>16 0000 0000 0
| 0000 1111 31
return i - (i >>> 1);
0000 1111 31
0000 0111 15
- 0000 1000 16
5.得到数组的下标(以前使用 index=num%数组的长度 效率低下)
保证两个条件:
*index的范围应为[0,len-1]
*均匀分布在[0,len-1] 的范围上
首先计算要 重新的 哈希数
final int hash(Object k){
int h=hashSeed;// hashSeed 默认为0
if(h!=0&& k instanceof String){
return sun.misc.Hashing.StringHash32((Strng) k);
}
h^=k.hashCode(); // 0^任意数都是本身
h^=(h>>>20)^(h>>>12);
return h^(h>>>7)^(h>>>4);
}
上面为什么 要进行这么多的 右移运算 和异或运算?
* 因为计算下标的时候, 它只和哈希后面的几位数有关,和其他前面的 哈希数都没有关系了,这样导致 哈希冲突比较重
所以我们要 让哈希函数的高位 参与到 运算中, 所以要右移
再计算 下标
static int indexFor(int h,int length){
return h&(length-1);
}
h: 0101 0101 length:16 length-1:15
15: 0000 1111
& 0000 0101 (竟然和原本的 h 的最后四位 一样, 这就是位运算的魅力)
*计算出来的结果的范围是 [0000,1111] 的 即就是 [0,15]
(第一个好处)
** 我们前面保证 数组的长度必须是 2的幂次方, 就是因为 h&数组的长度-1 刚刚好可以保证 index的范围应为[0,len-1]
* 而 h 就是 利用 哈希函数计算出来的, 具有哈希函数的性质。
6. key 为 null
*如果key==null 哈希函数就不会 在调用方法, 而是直接把 它封装成一个对象 放在 数组下标为0 的位置
7. 扩容
0.75(加载因子)
threshold(阈值)=table.length*0.75;
threshold=16*0.75=12;
*只有当前整个HashMap里面的数(size)>= threshold(阈值) 它才会扩容。
* 其实还有一个条件(一般没注意) table[bucketIndex]!=null (JDK7才会有这个条件)
就是说当前这个值 所定位的数组里面有元素才会,扩容。
扩展机制: 新的数组的长度是旧的数组的2倍
如何把旧数组复制到新数组?
*需要一个 双重循环, 访问到每一个元素,把每一个元素的hash重新计算出来
*因为 它计算到下标 利用了数组的长度的,因此我们要重新计算每一个元素的下标
细节:
其实它计算出来只有两种情况
第一种:在原来的位置。
第二种:在原来的位置+oldTable.length
举例说明:
oldTable.length=16;
一个数的 二进制为:0101 0101
15: 0000 1111
&
数: 0101 0101
0000 0101
数组的长度增长为32后:
31:0001 1111
.
& .
数:0101 0101
.
.
0001 0101
.
.
其实只和数的 低五位有关系,
如果 它的低五位是0,则和原来的下标一样。
如果 它的低五位是1, 则为 原来的小标+ oldTable.lenght
扩容的目的:是为了让链表变短。
8.线程
*会出现循环链表