今天看hashmap的源码,其中大量用到了位运算符,在此总结一下。
Java的位运算符主要有:&
、|、^、~、<<、>>、>>>。
1,&(位与) 作用是对运算符两侧以二进制表达的操作符按位分别进行'与'运算。而这一运算是以数中同样的位(bit)为单位的。规则是:仅当两个操作数都为1时,结果才为1。否则为0,示例:
10 的二进制为 1010, 3 的二进制为 0011。则 10 & 3 的二进制为 0010,则10 & 3 = 2
2,|(位或)
作用是对运算符两侧以二进制表达的操作符按位分别进行'或'运算。规则是:仅当两个操作数都为0时,结果才为0。否
则为1,示例
10 的二进制为 1010, 3 的二进制为 0011。则 10 | 3 的二进制为 1011,则10 | 3 = 11
3,^(位
异或)
作用是对运算符两侧以二进制表达的操作符按位分别进行'异或'运算。规则是:仅当两个操作数不同时,结果才为1。否
则为0,示例
10 的二进制为 1010, 3 的二进制为 0011。则 10 ^3 的二进制为 1001,则10 ^ 3 = 9
4,~(取反)
'取反'运算符~的作用是将各位数字取反:全部的0置为1
,
1置为0
,
示例:
10 的二进制为 1010,~10的二进制为11111111 11111111 11111111 11110101,~10=-11
5,
<<(左移)
左移就是把一个数的全部位数都向左移动若干位
,右边低位补0,示例:
10 的二进制为 1010,10 <<2 的二进制为 100100,则10 <<2 = 40,值等同于乘以Math.power(2,移动位数),但含义不同;
6,>>(有符号右移)
有符号右移
就是把一个数的全部位数都向右移动若干位
,当为正数时,左边高位补0;负数时补1。
示例:
10 的二进制为 1010,10 >>2 的二进制为 0010,则10 >>2 = 2,值等同于除以Math.power(2,移动位数),但不能整除时向下取 整,-10>>2=-3.
7,>>>(无符号右移)
无符号右移
就是把一个数的全部位数都向右移动若干位
,无论正负左边高位统一补0,示例:
10 的二进制为 1010,10 >>>2 的二进制为 0010,则10 >>2 = 2,-10>>>2=1073741821;
HashMap中的位运算符:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
上边这段代码是根据key来得到桶的hash值,右位移16位,正好是32bit的一半,自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。
/**
* Returns a power of two size for the given target capacity.
* -3 -> 0
* 0 -> 0
* 2 -> 2
* 3 -> 4
* 10 -> 16
* 15 -> 16
* 17 -> 32
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
传入的容量无论是处于任何范围,最终都会被打造成比该值大并且比最近的一个 2 的 n 次幂小一的值。为什么这么做?因为 2 的 n 次幂小一的值在二进制角度看全为 1,将有利于 HashMap 中的元素搜索。
Ps: HashMap的数据结构已经由原来的数组链
表Node[] 变成了数组 链表+红黑树(链表长度>=8-1)