Integer.highestOneBit(int i)方法的作用與底層實現


在Integer類中有這么一個方法,你可以給它傳入一個數字,它將返回小於等於這個數字的一個2的冪次方數。這個方法就是highestOneBit(int i)。

比如下面的Demo,注意方法的輸入與返回值:

System.out.println(Integer.highestOneBit(15));  // 輸出8
System.out.println(Integer.highestOneBit(16));  // 輸出16
System.out.println(Integer.highestOneBit(17));  // 輸出16

這個方法的實現代碼量也是非常少的:

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);
}

接下來,我們就來詳細分析一下這塊代碼的邏輯。

首先,對於這個方法的功能:給定一個數字,找到小於或等於這個數字的一個2的冪次方數。

如果我們要自己來實現的話,我們需要知道:怎么判斷一個數字是2的冪次方數。

說真的,我一下想不到什么好方法來判斷,唯一能想到的就是一個數字如果把它轉換成二進制表示的話,它會有一個規律:如果一個數字是2的冪次方數,那么它對應的二進制表示僅有一個bit位上是1,其他bit位全為0。
比如:
十進制6,二進制表示為:0000 0110
十進制8,二進制表示為:0000 1000
十進制9,二進制表示為:0000 1001
所以,我們可以利用一個數字的二進制表示來判斷這個數字是不是2的冪次方數。關鍵代碼怎么實現呢?去遍歷每個bit位?可以,但是不好,那怎么辦?我們還是回頭仔細看看Integer是如何實現的吧?

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);
}

我們發現這段代碼中沒有任何的遍歷,只有位運算與一個減法,也就是說它的實現思路和我們自己的實現思路完全不一樣,它的思路就是:給定一個數字,通過一系列的運算,得到一個小於或等於該數字的一個2的冪次方數。

也就是:如果給定一個數字18,通過運算后,要得到16。

18用二進制表示為: 0001 0010

想要得到的結果(16)是:0001 0000

那么這個運算的過程無非就是將18對應的二進制數中除最高位的1之外的其他bit位都清零,則拿到了我們想要的結果。

那怎么通過位運算來實現這個過程呢?

我們拿18對應的二進制數0001 0010來舉個例子就行了:
先將0001 0010右移1位,
得到0000 1001,再與自身進行或運算:
得到0001 1011

再將0001 1011右移2位,
得到0000 0110,再與自身進行或運算:
得到0001 1111

再將0001 1111右移4位,
得到0000 0001,再與自身進行或運算:
得到0001 1111

再將0001 1111右移8位,
得到0000 0000,再與自身進行或運算:
得到0001 1111

再將0001 1111右移16位,
得到0000 0000,再與自身進行或運算:
得到0001 1111

再將0001 1111無符號右移1位,
得到0000 1111

關於無符號右移,可以看我之前寫的文章。

最后用0001 1111 - 0000 1111 = 0001 0000
震驚!得到了我們想要的結果。

其實這個過程可以抽象成這樣:
現在有一個二進制數據,0001****,我們不關心低位的取值情況,我們對其進行右移並且進行或運算。

先將0001****右移1位,
得到00001***,再與自身進行或運算:
得到00011***

再將00011***右移2位,
得到0000011*,再與自身進行或運算:
得到0001111*

再將0001111*右移4位,
得到00000001,再與自身進行或運算:
得到00011111

后面不用再推算了,到這里我們其實可以發現一個規律:
右移與或運算的目的就是想讓某個數字的低位都變為1,再用該結果 減去 該結果右移一位后的結果,則相當於清零了原數字的低位。即得到了我們想要的結果。

到此,只能感嘆JDK作者對於位運算的使用已經達到了出神入化的境界了。

如果想學習更多的精彩的Java、分布式、微服務等方面的知識,請關注微信公眾號:1點25

reny125.jpeg


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM