http://www.javashuo.com/article/p-tyhrgxud-dn.htmlhtml
函數的做用是得到傳入參數的最高位的1,對於正數來講返回值爲小於i的最大二次冪,對於負數來講永遠是負數的最大值即-2^31
java
例如:7=0000 0111(省略前24位0)那麼函數的返回值爲 0000 0100=4算法
一般來講最直觀的作法就是暴力法,我一個一個數不就行了segmentfault
//一位一位取就是了 public int heigestOneBit(int i){ int res=1; if (i<0)return Integer.MIN_VALUE; while(i!=0){ if (i!=1){ res*=2; } i/=2; } return res; }
看看JDK如何利用更加高效的位操做實現這一個函數函數
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); }
爲何JDK一通位操做就把最高位取出來了呢?很是的巧妙啊,舉例說明,看如下就知道了優化
//如下以數字爲例,其中x表示0或者1不影響結果,1-最高位的1 i 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx i>>1 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx i|=(i>>1) 0000 0011 xxxx xxxx xxxx xxxx xxxx xxxx i 0000 0011 xxxx xxxx xxxx xxxx xxxx xxxx i>>2 0000 0000 11xx xxxx xxxx xxxx xxxx xxxx i|=(i>>2) 0000 0011 11xx xxxx xxxx xxxx xxxx xxxx i 0000 0011 11xx xxxx xxxx xxxx xxxx xxxx i>>4 0000 0000 0011 11xx xxxx xxxx xxxx xxxx i|=(i>>4) 0000 0011 1111 11xx xxxx xxxx xxxx xxxx i 0000 0011 1111 11xx xxxx xxxx xxxx xxxx i>>8 0000 0000 0000 0011 1111 11xx xxxx xxxx i|=(i>>8) 0000 0011 1111 1111 1111 11xx xxxx xxxx i 0000 0011 1111 1111 1111 11xx xxxx xxxx i>>16 0000 0000 0000 0000 0000 0011 1111 1111 i|=(i>>16) 0000 0011 1111 1111 1111 1111 1111 1111 i 0000 0011 1111 1111 1111 1111 1111 1111 i>>>1 0000 0001 1111 1111 1111 1111 1111 1111 i-(i>>>1) 0000 0010 0000 0000 0000 0000 0000 0000
看完上面的簡單分析應該就知道JDK如何實現的了,簡單來講就是把第一個1不斷日後移動,使得從第一個1以後的全部比特位都爲1,此時減去右移一位的值,也就是減去後面全部的1表明的值,此時天然只剩下第一個1了,能夠說很是的巧妙了code
該方法的做用是統計一個整數的二進制表示形式中1的個數,沒記錯的話這其實也是leetcode中的一道題htm
首先仍是咱們本身來思考一下如何實現:blog
一個bit一個bit計數leetcode
public static int bitCount(int i){ //暴力法 int count=0; while(i!=0){ if ((i&1)==1){ count++; } i=i>>>1; } return count; }
試想對於二進制 100,1的個數爲1,按照暴力法須要3次才能統計出來,怎麼樣一次統計出來呢,也就是怎麼一次就把100變成0呢?
對於1xxx這樣的數字,x表明0,以100爲例,100-1=011,而100&011剛好爲0,能作多少次這樣的運算,它就有多少位1,代碼以下
public static int bitCount(int i){ //位運算優化 int count=0; while(i!=0){ i=i&(i-1); count++; } return count; }
到這裏,有多少位的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; }
Superise Mother Fxxk!這是在幹什麼?首先解釋一下整體的思想
//要統計以下二進制的:1001 1010 1010 1010 的1的位數 //JDK是這樣作的 先每兩個bit統計有多少個1,而後就保存在二進制的本地 10 01 10 10 10 10 10 10 01 01 01 01 01 01 01 01 而後再統計連續四個bit有多少個1,而後保存在本地 0010 0010 0010 0010 再統計8個bit有多少個1,保存在本地 0000 0100 0000 0100 而後再統計每16個比特有多少個1,保存再本地 0000 0000 0000 1000 ==8總共8個1
有了總體的算法思想,來看看這幾個奇怪的數字0x55555555
、0x33333333
、0x0f0f0f0f
他們對應的二進制以下:
0x55555555 01010101010101010101010101010101 0x33333333 00110011001100110011001100110011 0x0f0f0f0f 00001111000011110000111100001111
針對0x55555555
來看看效果,怎麼把兩個相鄰bit位中的1存儲下來,
//以12345爲例 12345 0000 0000 0000 0000 0011 0000 0011 1001 0x55555555 0101 0101 0101 0101 0101 0101 0101 0101 12345& 0x55555555 0000 0000 0000 0000 0001 0000 0001 0001 //能夠看到至關於把兩個相鄰的比特位的後一位的1所有取出來了 12345>>>1 0000 0000 0000 0000 0001 1000 0001 1100 0x55555555 0101 0101 0101 0101 0101 0101 0101 0101 12345>>>1 &0x55555555 0000 0000 0000 0000 0001 0000 0001 0100 //能夠看到至關於把兩個相鄰的比特位的前一位的1所有取出來了 12345 00 00 00 00 00 00 00 00 00 11 00 00 00 11 10 01 last 1 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 01 first 1 00 00 00 00 00 00 00 00 00 01 00 00 00 01 01 00 last1+fisrt1 00 00 00 00 00 00 00 00 00 10 00 00 00 10 01 01 //能夠看到兩位中的1的數量已經用兩個bit來保存了
算法實現以下:
public static int bitCount(int i) { i = (i & 0x55555555) + ((i >>> 1) & 0x55555555); i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); i = (i & 0x0f0f0f0f) + ((i >>> 4) & 0x0f0f0f0f); i = (i & 0x00ff00ff) + ((i >>> 8) & 0x00ff00ff); i = (i & 0x0000ffff) + ((i >>> 16) & 0x0000ffff); return i; }
JDK再作點優化便可
上面兩個方法均在HashMap(JDK7實現)中的roundroundUpToPowerOf2方法中被調用,HashMap的分析詳見http://www.javashuo.com/article/p-qcwimtzs-nv.html,仍是很是有意思的