在閱讀源碼的過程中,經常會看到這些符號<< ,>>,>>>,這些符號在Java中叫移位運算符,在寫代碼的過程中,雖然我們基本上不會去寫這些符號,但需要明白這些符號的運算原理,比如HashMap中有以下代碼:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//左移 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//無符號右移 }
上段代碼中就包含左移運算符<<,無符號右移運算符>>>。這篇文章詳細說一下這三個符號:
1、左移運算符:<<
先隨便定義一個int類型的數int,十進制的value = 733183670,轉換成二進制在計算機中的表示如下:
value << 1,左移1位
左移1位后換算成十進制的值為:1466367340,剛好是733183670的兩倍, 有些人在乘2操作時喜歡用左移運算符來替代。
value << 8,左移8位看一下:
左移8位后變成了十進制的值為:-1283541504,移動8位后,由於首位變成了1,也就是說成了負數,在使用中要考慮變成負數的情況。
根據這個規則,左移32位后,右邊補上32個0值是不是就變成了十進制的0了?答案是NO,當int類型進行左移操作時,左移位數大於等於32位操作時,會先求余(%)后再進行左移操作。也就是說左移32位相當於不進行移位操作,左移40位相當於左移8位(40%32=8)。當long類型進行左移操作時,long類型在二進制中的體現是64位的,因此求余操作的基數也變成了64,也就是說左移64位相當於沒有移位,左移72位相當於左移8位(72%64=8),寫一段代碼來測試一下
int intValue = 733183670;//隨意寫一個數 System.out.println("intValue:" + (intValue));//打印intValue System.out.println("intValue左移1位:" + (intValue << 1));//左移1位 System.out.println("intValue左移8位:" + (intValue << 8));//左移8位 //當int類型左移位數大於等於32位操作時,會先求余后再進行移位操作 System.out.println("intValue左移32位:" + (intValue << 32));//求余為32%32=0,相當於左移0位(不移位) System.out.println("intValue左移40位:" + (intValue << 40));//求余為40%32=8,相當於左移8位 System.out.println("intValue左移64位:" + (intValue << 64));//求余為64%32=0,相當於左移0位(不移位) long longValue = 733183670L; System.out.println("longValue:" + (longValue));//打印longValue System.out.println("longValue左移1位:" + (longValue << 1));//左移1位 System.out.println("longValue左移8位:" + (longValue << 8));//左移8位 //當long類型左移位數大於等於64位操作時,會先求余后再進行移位操作 System.out.println("longValue左移64位:" + (longValue << 64));//求余為64%64=0,相當於左移0位(不移位) System.out.println("longValue左移72位:" + (longValue << 72));//求余為72%64=8,相當於左移8位 System.out.println("longValue左移128位:" + (longValue << 128));//求余為128%64=0,相當於左移0位(不移位)
看一下結果:
由於double,float在二進制中的表現比較特殊,因此不能來進行移位操作,報錯,編譯不過,如下圖:
注意:其它幾種整形byte,short移位前會先轉換為int類型(32位)再進行移位,這里就不寫代碼測試了,大家有興趣可自行測試。
綜上所述:左移 << 其實很簡單,也就是說丟棄左邊指定位數,右邊補0。
2、右移運算符:>>
還是這個數:733183670
value >> 1,右移1位
右移1位后換算成十進制的值為:366591835,剛好是733183670的1半, 有些人在除2操作時喜歡用右移運算符來替代
value >> 8,右移8位看一下
寫一段代碼測試一下
int intValue = 733183670;//隨意寫一個數 System.out.println("intValue:" + (intValue));//打印intValue System.out.println("intValue右移1位:" + (intValue >> 1));//右移1位 System.out.println("intValue右移8位:" + (intValue >> 8));//右移8位 //當int類型右移位數大於等於32位操作時,會先求余后再進行移位操作 System.out.println("intValue右移32位:" + (intValue >> 32));//求余為32%32=0,相當於右移0位(不移位) System.out.println("intValue右移40位:" + (intValue >> 40));//求余為40%32=8,相當於右移8位 System.out.println("intValue右移64位:" + (intValue >> 64));//求余為64%32=0,相當於右移0位(不移位) long longValue = 733183670L; System.out.println("longValue:" + (longValue));//打印longValue System.out.println("longValue右移1位:" + (longValue >> 1));//右移1位 System.out.println("longValue右移8位:" + (longValue >> 8));//右移8位 //當long類型右移位數大於等於64位操作時,會先求余后再進行移位操作 System.out.println("longValue右移64位:" + (longValue >> 64));//求余為64%64=0,相當於右移0位(不移位) System.out.println("longValue右移72位:" + (longValue >> 72));//求余為72%64=8,相當於右移8位 System.out.println("longValue右移128位:" + (longValue >> 128));//求余為128%64=0,相當於右移0位(不移位)
結果:
和左移一樣,int類型移位大於等於32位時,long類型大於等於64位時,會先做求余處理再位移處理,byte,short移位前會先轉換為int類型(32位)再進行移位。以上是正數的位移,我們再來看看負數的右移運算,如圖,負數intValue:-733183670的二進制表現如下圖:
右移8位,intValue >> 8
綜上所述:右移運算符>>的運算規則也很簡單,丟棄右邊指定位數,左邊補上符號位。
3、無符號右移運算符:>>>
無符號右移運算符>>>和右移運算符>>是一樣的,只不過右移時左邊是補上符號位,而無符號右移運算符是補上0,也就是說,對於正數移位來說等同於:>>,負數通過此移位運算符能移位成正數。以-733183670>>>8為例來畫一下圖
無符號右移運算符>>的運算規則也很簡單,丟棄右邊指定位數,左邊補上0。
摘自https://zhuanlan.zhihu.com/p/30108890