位运算大家都学过,不过应该在工程实践中应用都�比较少。不过这两天看了HashMap的内部实现,发现其中对于移位、与、或的使用非常的多,简直炫技。
HashMap的核心当然是hash方法,看看HashMap中如何去算新插入Object的index:
int index = hash & (tab.length - 1);
不知道大家看懂没有,这里需要一个上下文,就是HashMap的长度都是2的幂。而对于2的幂,再做-1之后,二进制位上全部是1,这时和h做与操作,相当于截取了h的后面位(假设length为2的n次方)。比如:
h = 69, 转换为二进制是:1000101;
length = 16,那么length - 1 =15转换为二进制:111;
所以,h & (lenght - 1)的二进制结果为101,即5。
可以发现,这个方法其实就是最常见的取余哈希方法,而这里利用长度是2的幂这个特点,绕过了基于除法的取余方法,大大提高了效率。
再举一个例子,在HashMap中用到了Collections的一个静态方法:
/**
* Returns the smallest power of two >= its argument, with several caveats:
* If the argument is negative but not Integer.MIN_VALUE, the method returns
* zero. If the argument is > 2^30 or equal to Integer.MIN_VALUE, the method
* returns Integer.MIN_VALUE. If the argument is zero, the method returns
* zero.
* @hide
*/
public static int roundUpToPowerOfTwo(int i) {
i--; // If input is a power of two, shift its high-order bit right.
// "Smear" the high-order bit all the way to the right.
i |= i >>> 1;
i |= i >>> 2;
i |= i >>> 4;
i |= i >>> 8;
i |= i >>> 16;
return i + 1;
}
这个方法用来返回大于等于当前数字的最小2的幂,原理其实很简单,将一个二进制数的首位之后所有的位都置为1,再将这个中间值加1,就得到了我们所期望的值。而这里他使用的是一种辗转求或的方法,两位、四位、八位……逐渐的将各数位变为1。因为int最大就是2^32,所以只用到做到i |= i >>> 16。而考虑到如果输入就是2的幂,需要先做一步自减的操作。
除了我列的这个,HashMap以及其他文件源码里还有很多位运算的例子,这里只是抛砖引玉,希望大家多研究探讨~