Integer中用到了很多位运算,而java整数的固定长度也为位运算提供不少便利性,下面对Integer中的经典方法做分析(这些方法基本上都用到了位运算,原因很简单:高效,并行)
1.求整数中二进制1的个数
public static intbitCount(int i) {
// HD, Figure 5-2
i= i - ((i >>> 1) & 0x55555555);
i= (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i= (i + (i >>> 4)) & 0x0f0f0f0f;
i= i + (i >>> 8);
i= i + (i >>> 16);
returni & 0x3f;
}
分析:该方法仅需五步完成,时间复杂度是O(logN)(N是整数的二进制长度),算法妙在二进制并行运算,之后的算法很多也用到了并行运算。
分解:
第一步:求每两位中1的个数之和,比如i=(01)( 11)( 00)( 10)( 11)( 01)( 11)(01)->(01)(10)(00)(01)(10)(01)(10)(01)
第二步:求前面结果的每四位之和(0110)(0001)(1001)(1001)->(0010)(0001)(0010)(0010)
第三步:同上道理,求每8位置和
。。。。
并行:比如求没两位之和时,不用先等前面两位算完再算后面两位,直接同时进行,这也是位运算一大特色
2.返回具有至多单个1 位的 int 值,在指定的 int 值中最高位(最左边)的 1 位的位置
public static inthighestOneBit(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);
}
分析:该方法时间复杂度是O(logN)
分解:
第一步:让整数二进制最高位为1的右边一位也为1
第二步:让整数二进制最高位为1的右边3位也为1
第三步:让整数二进制最高位为1的右边7位也为1
。。。。
最后整数二进制最高位为1的右边全为1,然后通过运算只保留最高位的1
3.返回具有至多单个 1 位的 int 值,在指定的 int 值中最低位(最右边)的 1位的位置
public static intlowestOneBit(int i) {
// HD, Section 2-1
return i & -i;
}
分析:该方法一步完成,妙哉!
4.在指定 int 值的二进制补码表示形式中最高位(最左边)的 1 位之前,返回零位的数量
public static intnumberOfLeadingZeros(int i) {
// HD, Figure 5-6
if (i == 0)
return 32;
int n = 1;
if (i >>> 16 == 0) { n += 16;i <<= 16; }
if (i >>> 24 == 0) { n +=8; i <<=8; }
if (i >>> 28 == 0) { n +=4; i <<=4; }
if (i >>> 30 == 0) { n +=2; i <<=2; }
n -= i >>> 31;
return n;
}
分析:时间复杂度是O(logN),采用了类似二分查找的思想
5.返回指定的 int 值的二进制补码表示形式中最低(“最右”)的为 1 的位后面的零位个数
public static intnumberOfTrailingZeros(int i) {
// HD, Figure 5-14
inty;
if(i == 0) return 32;
intn = 31;
y= i <<16; if (y != 0) { n = n -16; i = y; }
y= i << 8; if (y != 0) { n = n - 8; i = y; }
y= i << 4; if (y != 0) { n = n - 4; i = y; }
y= i << 2; if (y != 0) { n = n - 2; i = y; }
returnn - ((i << 1) >>> 31);
}
分析:类似上一方法
6.反转二进制整数
public static intreverse(int i) {
// HD, Figure 7-1
i= (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
i= (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
i= (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
i= (i << 24) | ((i & 0xff00) << 8) |
((i >>> 8) & 0xff00) | (i>>> 24);
returni;
}
分析:时间复杂度是O(logN),采用分治法,先局部后整体
分解:第一步:每相邻两位互换
第二步:每相邻四位互换,四位内部不互换
…..
7.获得整数征服符号表示:1,0,-1
public static intsignum(int i) {
// HD, Section 2-7
return (i >> 31) | (-i>>> 31);
}