JDK1.8对ConcurrentHashMap进行了一些改动,因此照着源码和网上博客看看究竟有哪些改动?还没看一小会儿,就发现了一个方法比较硬咬不动,因此研究了一下,并记录下对该方法的理解。
ConcurrentHashMap有一个构造函数,请看下面!
public ConcurrentHashMap(int initialCapacity) {
if (initialCapacity < 0)
throw new IllegalArgumentException();
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
this.sizeCtl = cap;
}
它是一个带有参数的构造函数,可以指定ConcurrentHashMap初始化的大小,但是由于“神秘的里力量”,ConcurrentHashMap初始化大小始终为2幂次(为什么需要初始化成2的幂次呢?),例如:
1. Map map = new ConcurrentHashMap(2) 其实初始化的大小为4
2. Map map = new ConcurrentHashMap(14) 其实初始化的大小为32
3. Map map = new ConcurrentHashMap(16) 其实初始化的大小为32
4. Map map = new ConcurrentHashMap(128) 其实初始化的大小为256
是谁,将初始化的大小变为2的幂次,是“他”,本篇主角 tableSizeFor(int c),“他”到底做了什么,先来看看方法的全貌:
/**
* Returns a power of two table size for the given desired capacity.
*/
private static final int tableSizeFor(int c) {
int n = c - 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;
}
注释讲得不是很中国化,可能需要百度翻译一下:
Returns a power of two table size for the given desired capacity.
中译
给定一个期望的初始化大小值,返回一个2次幂大小值
咋一看这个方法不是很懂,很(感)不(觉)喜(很)欢(高)看(大)移(上)位(的)操(样)作(子),看的一脸懵逼!
于是仔细研究了一下,下面发现很巧妙!下面解析一下,“他”是如何做到的。我们知道JAVA当中的int基本数据类型所占的字节数是4Bytes,也就是32bits;>>> or <<<在JAVA中表示无符号移位。好的知道这些就可以开工啦!!!
1.试想一下如果c = 0
也就是下面这个样子
c:0000 0000 0000 0000 0000 0000 0000 0000 32bits
给方法里面的代码做上标记:
(1)int n = c - 1;
(2)n |= n >>> 1;
(3)n |= n >>> 2;
(4)n |= n >>> 4;
(5)n |= n >>> 8;
(6)n |= n >>> 16;
(7)return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
执行(1)之后c < 0
很显然最后返回的结果为1
2.如果c > 0
也就是下面这样
c:0000 0000 0000 0000 0010 0000 0000 0010 32bits
or
c:1000 0000 0000 0000 0010 0000 0000 0010 32bits
or
c:0000 0000 0000 0100 0010 0000 1000 1010 32bits
...
可以发现如果c > 0
话,就一定会出现一个分割点1
在它的左边全是0
或者没有数据了。如果我们将这个1
往右移一位也就是代码(2),再与n
本身|
一下,那么n
中至少有2
个1
,也就是下面c3
标红的两个1
举例:
c1:0000 0000 0000 0100 0010 0000 1000 1010 32bits
c2: 000 0000 0000 0010 0001 0000 0100 01010 32bits c1移一位
c3:0000 0000 0000 0110 0011 0000 1100 1111 32bits c1与c2或
接上c3
执行代码(3)也就是c3
两个红11
右移两位(也就是c4
)
c3:0000 0000 0000 0110 0011 0000 1100 1111 32bits
c4: 00 0000 0000 0001 1000 1100 0011 0011 11 32bits
c5:0000 0000 0000 0111 1011 1100 1111 1111 32bits c3与c4或
如果n是够大的话,那么n是不是最少有四个1
,然后执行(4)就是8个1,(5)就是16个1,(6)就是32个1,最终执行完(2)(3)(4)(5)(6),n如果够大是不是32位都是1
。因此,如果n > 0
,经过(2)(3)(4)(5)(6)这五步,n所有有效位都能置为1
,最后执行(7)就是2次幂的数了。