java 0x3f_Java源码位操作技巧欣赏
最近有人問些邏輯位移,算術位移的區別和應用場景,翻了一些舊材料,重新整理一下發出來,給大家欣賞一下java源碼中涉及的位操作技巧,摘取的是Integer的源碼實現。
整數二進制左邊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);
}
畫個圖簡單說明一下計算的原理,簡化一下只用8位為例,做了右移1,2,4位之后的效果,最終的效果就是在最早出現1的位置后面都是1。 32位的int也是類似的,最終的效果就是從左邊第一位是1開始后面都是1。
有點神奇,仔細想想,前面的多次移位就是最前面那個1填滿后面所有位(無論后面是0還是1)。
整數二進制右邊1最早出現的位置
public static int lowestOneBit(int i) {
// HD, Section 2-1
return i & -i;
}
負數以補碼形式存在,一個負數的補碼,就是對應整數的二進制再取反加一。i和-i總有一個是正數,假設是i吧,由于兩個數相加是0,在二進制角度來看,假設i最右邊出現1的位置是k,那么在相同位置上-i必定也是1,并且-i在k以后的位也是0,這2位相加是0,并且進位1,那么要是k-1位相加得到0,兩個數字在k-1位上只能是一個1,另外一個是0,以此類推,可以得到從左邊第一位開始直到k-1位都是不同的。兩個數進行與運算,只會在k位保留1,其他位都變成0。
整數二進制左邊開頭有連續多少個0
public static int numberOfLeadingZeros(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;
}
以移動16位為例子,如果左移16位之后是0,那么前16位都是0,那么就只要判斷后面16位就可以了。 如果不是0,那么只要判斷前面16位就可以了,和前面的區別就是,它不需要移位。后面的移位操作,以此類推就可以了。
整數二進制右邊結束有連續多少個0
public static int numberOfTrailingZeros(int i) {
// HD, Figure 5-14
int y;
if (i == 0) return 32;
int n = 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; }
return n - ((i << 1) >>> 31);
}
和上面的差不多,假設右移16位不等于0,那么說明只需要判斷右邊16位就可以了。如果等于0,那說明要判斷左邊16位。后面的移位操作,以此類推就可以了。
整數二進制總共有多少個1
public static int bitCount(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);
return i & 0x3f;
}
這個算法我是看呆了,神乎其技,它的做法是分別計算每2位,每4位,每8位,每16位,每32位..這樣的順序計算。
以第一行為例,0x55555555剛好每2位是01,而每2位有4種情況,分別是00,01,10,11, 計算i-((i>>>1) & 01) 可以得到00,01,01,10, 就對應表示1的個數。是不是很神奇。
整數向左滾動X位
public static int rotateLeft(int i, int distance) {
return (i << distance) | (i >>> -distance);
}
這是基本的移位操作,注意的是右邊要采用算術右移。
整數向右滾動X位
public static int rotateRight(int i, int distance) {
return (i >>> distance) | (i << -distance);
}
和上面的一樣,就不再詳述了。
整數二進制反轉
public static int reverse(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);
return i;
}
這個代碼也很神奇,完全看不懂。以第一行為例,它的作用就是兩兩交換,可以通過下圖分析可得。
同理,第二行是每2位交換,第三行是每4位交換,假設原來是b1b2b3b4b5b6b7b8,那么三行執行后,結果就是b8b7b6b5b4b3b2b1,就是一個字節的位進行反轉了。最后一句就是按字節反轉一下,最終結果就是按位反轉了。
整數的符號,0返回0,正數返回1,負數返回-1
public static int signum(int i) {
// HD, Section 2-7
return (i >> 31) | (-i >>> 31);
}
感覺這個實現有點多此一舉,直接大小判斷很簡單,也很直觀,為什么不采用?
還是畫個圖說明一下吧。
整數按字節反轉
public static int reverseBytes(int i) {
return ((i >>> 24) ) |
((i >> 8) & 0xFF00) |
((i << 8) & 0xFF0000) |
((i << 24));
}
這個算法很好理解,第一個字節右移24位就跑到第四個字節去了,第二個字節右移8位到了第三位,和FF00與運算就抹掉對第四個字節的影響。后面的就不解釋了。
從Integer的方法實現看,有些位操作的技巧實在很巧妙,能想出來真的不容易。像java之類的源代碼還是有很多有趣,實用的技巧的,有機會再分享一些。
總結
以上是生活随笔為你收集整理的java 0x3f_Java源码位操作技巧欣赏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot 整合 mongod
- 下一篇: java弃用标签_Java 9 揭秘(1