最近工作中被運(yùn)算效率問(wèn)題所困擾,比如大數(shù)據(jù)排序或者去重腌且,因此現(xiàn)在需要補(bǔ)習(xí)一下位移運(yùn)算元践。
首先講一下位移概念片酝?
左位移(<<):將運(yùn)算符左邊的對(duì)象向左移動(dòng)運(yùn)算符右邊指定的位數(shù)(在低位補(bǔ)0) eg:x<<3
右位移(>>):"有符號(hào)"右移運(yùn)算 符,將運(yùn)算符左邊的對(duì)象向右移動(dòng)運(yùn)算符右邊指定的位數(shù)。使用符號(hào)擴(kuò)展機(jī)制篓吁,也就是說(shuō)莉擒,如果值為正挠唆,則在高位補(bǔ)0婚脱,如果值為負(fù),則在高位補(bǔ)1. eg:x>>3
無(wú)符號(hào)右移位(>>>):"無(wú)符號(hào)"右移運(yùn)算 符重付,將運(yùn)算符左邊的對(duì)象向右移動(dòng)運(yùn)算符右邊指定的位數(shù)顷级。采用0擴(kuò)展機(jī)制,也就是說(shuō)确垫,無(wú)論值的正負(fù)弓颈,都在高位補(bǔ)0?x>>>3
以int類型的6297為例拣凹,代碼如下:
[java]view plaincopy
System.out.println(Integer.toBinaryString(6297));???
System.out.println(Integer.toBinaryString(-6297));???
System.out.println(Integer.toBinaryString(6297>>5));???
System.out.println(Integer.toBinaryString(-6297>>5));???
System.out.println(Integer.toBinaryString(6297>>>5));???
System.out.println(Integer.toBinaryString(-6297>>>5));???
System.out.println(Integer.toBinaryString(6297<<5));???
System.out.println(Integer.toBinaryString(-6297<<5));??
? 運(yùn)行結(jié)果:
1100010011001
11111111111111111110011101100111
11000100
11111111111111111111111100111011
11000100
111111111111111111100111011
110001001100100000
11111111111111001110110011100000
注:x<>y相當(dāng)于x/2y
??? 從計(jì)算速度上講,移位運(yùn)算要比算術(shù)運(yùn)算快恨豁。
??? 如果x是負(fù)數(shù)嚣镜,那么x>>>3沒(méi)有什么算術(shù)意義,只有邏輯意義橘蜜。
在Think inJava中有這么一段話
“對(duì)char菊匿,byte或者short進(jìn)行移位處理,那么在移位進(jìn)行之前计福,它們會(huì)自動(dòng)轉(zhuǎn)換成一個(gè)int跌捆。只有右側(cè)的5個(gè)低位才會(huì)有用。這樣可防止我們?cè)谝粋€(gè)int數(shù)里移動(dòng)不切實(shí)際的位數(shù)象颖。若對(duì)一個(gè)long值進(jìn)行處理佩厚,最后得到的結(jié)果也是long。此時(shí)只會(huì)用到右側(cè)的6個(gè)低位说订,防止移動(dòng)超過(guò)long值里現(xiàn)成的位數(shù)抄瓦。”
這段話有兩個(gè)出處陶冷,一個(gè)是Java編程思想3.11移位操作符中出現(xiàn)钙姊,原話是“只有數(shù)值右端的低5位才有用”。一個(gè)是Java解惑中謎題27:變幻莫測(cè)的i值埂伦,原話是“移位操作符只使用其右操作數(shù)的低5位作為移位長(zhǎng)度”煞额。
弄清這句話首先需要弄清楚移位操作符,移位操作符是一個(gè)二元操作符沾谜,兩個(gè)操作數(shù)分別位于移位操作兩邊形如:左操作數(shù)?移位操作符?右操作數(shù)?這樣的結(jié)構(gòu)膊毁,其含義是,將左操作數(shù)按照移位操作符指定的移位方向基跑,進(jìn)行右操作數(shù)指定的次數(shù)的移位婚温。然后對(duì)照出處二,Java解惑中所描述的涩僻,就豁然開朗了缭召。
首先,移位操作符能操作的數(shù)只有int類型和long類型逆日,這個(gè)是指左操作數(shù)的類型。對(duì)于int類型而言萄凤,int在Java中占4字節(jié)室抽,一共32位,也就是說(shuō)靡努,對(duì)于一個(gè)在Java中的int數(shù)據(jù)坪圾,做32次移位晓折,那么這個(gè)int數(shù)據(jù)就完全變了,以左移為例兽泄,左移是補(bǔ)0漓概,那么對(duì)于任意一個(gè)int類型數(shù)據(jù),做32次移位病梢,那么int數(shù)據(jù)變成32位全0的數(shù)據(jù)胃珍,Java不允許一次性移位左操作數(shù)的所有位,也就是右操作數(shù)不能大于32蜓陌。于是回到上述的句子觅彰,其指的是右操作數(shù)的低5位,5位二進(jìn)制所代表的最大值為2^5-1钮热,為31填抬,所以取右操作數(shù)的低5位,就是只看右操作數(shù)的二進(jìn)制的低5位隧期,其數(shù)值不會(huì)超過(guò)2^5次方飒责,也就是int的32位。因此仆潮,移位操作符進(jìn)行移位的實(shí)際次數(shù)读拆,其實(shí)是右操作數(shù)2的次數(shù)。
對(duì)上面那段話的理解是:移位操作符操作的運(yùn)算對(duì)象是二進(jìn)制的“位”鸵闪,int類型是32位也就是2的5次冪 檐晕!如果移32位以上,那么原來(lái)的數(shù)的信息會(huì)全部丟失蚌讼,這樣也就沒(méi)有什么意義了辟灰!所以上面的“只有右側(cè)的5個(gè)低位才會(huì)有用”說(shuō)的是:移位操作符右端的那個(gè)數(shù)(化成二進(jìn)制)的低5位才有用,即
X < <y;
是指y的低5位才有用篡石,即不能大于32芥喇。 而對(duì)于long型也是同樣的道理!
因此凰萨,如果對(duì)一個(gè)int 型继控,進(jìn)行移位,X < <y; 當(dāng)y小于32時(shí)胖眷,移位后的結(jié)果一般都在我們的預(yù)料當(dāng)中武通;而如果y大于32時(shí),由于移位超出了int所能表示的范圍珊搀,這時(shí)就先把y化成二進(jìn)制數(shù)冶忱,然后取該二進(jìn)制數(shù)右端的低5位,再把這5位化成十進(jìn)制境析,此時(shí)的這個(gè)十進(jìn)制就是要對(duì)X移動(dòng)的位數(shù)囚枪。
例如:
intinta=140;
a << 34System.out.println(Integer.toBinaryString(a << b));
上面那兩個(gè)語(yǔ)句的執(zhí)行過(guò)程是:先把a(bǔ)化成二進(jìn)制數(shù):10001100
執(zhí)行語(yǔ)句 a << 34 對(duì)a左移32位時(shí)派诬,先把 34化成二進(jìn)制:100010,對(duì)該二進(jìn)制數(shù)取右邊5位链沼,即00010默赂,化成十進(jìn)制數(shù)為2,所以實(shí)際上是對(duì)a左移兩位±ㄉ祝現(xiàn)在缆八,地球人都會(huì)知道上面程序的輸出結(jié)果是:1000110000
//////////////////////////////////////////////////
移位運(yùn)算符和按位運(yùn)算符一樣,同屬于位運(yùn)算符朝刊,因此移位運(yùn)算符的位指的也是二進(jìn)制位耀里。它包括以下幾種:
左移位(<<):將操作符左側(cè)的操作數(shù)向左移動(dòng)操作符右側(cè)指定的位數(shù)?。移動(dòng)的規(guī)則是在二進(jìn)制的低位補(bǔ)0拾氓。
有符號(hào)右移位(>>):將操作符左側(cè)的操作數(shù)向右移動(dòng)操作符右側(cè)指定的位數(shù)冯挎。移動(dòng)的規(guī)則是,如果被操作數(shù)的符號(hào)為正咙鞍,則在二進(jìn)制的高位補(bǔ)0房官;如果被操作數(shù)的符號(hào)為負(fù),則在二進(jìn)制的高位補(bǔ)1续滋。
無(wú)符號(hào)右移位(>>>):將操作符左側(cè)的操作數(shù)向右移動(dòng)操作符右側(cè)指定的位數(shù)翰守。移動(dòng)的規(guī)則是,無(wú)論被操作數(shù)的符號(hào)是正是負(fù)疲酌,都在二進(jìn)制位的高位補(bǔ)0蜡峰。
注意,移位運(yùn)算符不存在“無(wú)符號(hào)左移位(<<<)”一說(shuō)朗恳。與按位運(yùn)算符一樣湿颅,移位運(yùn)算符可以用于byte、short粥诫、int油航、long等整數(shù)類型,和字符串類型char怀浆,但是不能用于浮點(diǎn)數(shù)類型float谊囚、double;當(dāng)然执赡,在Java5.0及以上版本中镰踏,移位運(yùn)算符還可用于byte、short搀玖、int余境、long、char對(duì)應(yīng)的包裝器類灌诅。我們可以參照按位運(yùn)算符的示例寫一個(gè)測(cè)試程序來(lái)驗(yàn)證芳来,這里就不再舉例了。
與按位運(yùn)算符不同的是猜拾,移位運(yùn)算符不存在短路不短路的問(wèn)題即舌。
寫到這里就不得不提及一個(gè)在面試題中經(jīng)常被考到的題目:
請(qǐng)用最有效率的方法計(jì)算出2乘以8等于幾?這里所謂的最有效率挎袜,實(shí)際上就是通過(guò)最少顽聂、最簡(jiǎn)單的運(yùn)算得出想要的結(jié)果,而移位是計(jì)算機(jī)中相當(dāng)基礎(chǔ)的運(yùn)算了盯仪,用它來(lái)實(shí)現(xiàn)準(zhǔn)沒(méi)錯(cuò)了紊搪。左移位“<<”把被操作數(shù)每向左移動(dòng)一位,效果等同于將被操作數(shù)乘以2全景,而2*8=(2*2*2*2)耀石,就是把2向左移位3次。因此最有效率的計(jì)算2乘以8的方法就是“2<<3”爸黄。
最后滞伟,我們?cè)賮?lái)考慮一種情況,當(dāng)要移位的位數(shù)大于被操作數(shù)對(duì)應(yīng)數(shù)據(jù)類型所能表示的最大位數(shù)時(shí)炕贵,結(jié)果會(huì)是怎樣呢梆奈?比如,1<<35=?呢称开?
這里就涉及到移位運(yùn)算的另外一些規(guī)則:
byte亩钟、short、char在做移位運(yùn)算之前鳖轰,會(huì)被自動(dòng)轉(zhuǎn)換為int類型清酥,然后再進(jìn)行運(yùn)算。 byte脆霎、short总处、int、char類型的數(shù)據(jù)經(jīng)過(guò)移位運(yùn)算后結(jié)果都為int型睛蛛。 long經(jīng)過(guò)移位運(yùn)算后結(jié)果為long型鹦马。
在左移位(<<)運(yùn)算時(shí),如果要移位的位數(shù)大于被操作數(shù)對(duì)應(yīng)數(shù)據(jù)類型所能表示的最大位數(shù)忆肾,那么先將要求移位數(shù)對(duì)該類型所能表示的最大位數(shù)求余后荸频,再將被操作數(shù)移位所得余數(shù)對(duì)應(yīng)的數(shù)值,效果不變客冈。
比如1<<35=1<<(352)=1<<3=8旭从。 對(duì)于有符號(hào)右移位(>>)運(yùn)算和無(wú)符號(hào)右移位(>>>)運(yùn)算,當(dāng)要移位的位數(shù)大于被操作數(shù)對(duì)應(yīng)數(shù)據(jù)類型所能表示的最大位數(shù)時(shí),那么先將要求移位數(shù)對(duì)該類型所能表示的最大位數(shù)求余后和悦,再將被操作數(shù)移位所得余數(shù)對(duì)應(yīng)的數(shù)值退疫,效果不變。鸽素。
比如100>>35=100>>(352)=100>>3=12褒繁。
? ? //以二進(jìn)制(基數(shù) 2)無(wú)符號(hào)整數(shù)形式返回一個(gè)整數(shù)參數(shù)的字符串表示形式。
? ? //如果參數(shù)為負(fù)馍忽,該無(wú)符號(hào)整數(shù)值為參數(shù)加上 2^32棒坏;否則等于該參數(shù)。? ? ? ? System.out.println(Integer.toBinaryString(-1)) ;
? ? ? ? System.out.println(Integer.toBinaryString(2)) ;
? ? ? ? System.out.println(Integer.toBinaryString(1)) ;
? ? 輸出:
? ? ? ? 11111111111111111111111111111111
? ? ? ? 11111111111111111111111111111110
? ? ? ? 1
結(jié)論輸出的是數(shù)字的二進(jìn)制補(bǔ)碼遭笋。為什么說(shuō)是以?二進(jìn)制無(wú)符號(hào)整數(shù)形式?返回一個(gè)?整數(shù)類型的字符串坝冕,為什么?如果參數(shù)為負(fù)數(shù),就要加上 232?次方瓦呼?
因?yàn)镴ava里的int是有符號(hào)的喂窟,在內(nèi)存中沒(méi)有正負(fù)之分,只有0/1吵血,整數(shù)是用補(bǔ)碼表示的
正數(shù)補(bǔ)碼等于原碼
負(fù)數(shù)的補(bǔ)碼等于其絕對(duì)值的反碼+1谎替,正好等于自身+2^32(對(duì)于4字節(jié)的整型來(lái)說(shuō))
-1的補(bǔ)碼 就是絕對(duì)值1的反碼(按位取反)11111111 11111111 11111111 11111110再+1
等于11111111 11111111 11111111 11111111
這樣正好能把最高位為1的數(shù)字用來(lái)表示負(fù)數(shù),而最高位為0的數(shù)字表示非負(fù)數(shù)
10000000 00000000 00000000 00000000 => -2147483648
11111111 11111111 11111111 11111111 => -1
00000000 00000000 00000000 00000000 => 0
00000000 00000000 00000000 00000001 => 1
01111111 11111111 11111111 11111111 => 2147483647
因此負(fù)數(shù)+2^32之后的二進(jìn)制串蹋辅,就是該負(fù)數(shù)內(nèi)存中準(zhǔn)確的存儲(chǔ)形式
對(duì)于上面的操作???a << 34 對(duì)a左移32位 有的人會(huì)有疑問(wèn)钱贯?不是應(yīng)該左移34位嗎?二進(jìn)制數(shù)取右邊5位侦另,為什么是右5位呢秩命? 為了解答你的問(wèn)題,請(qǐng)看下面:
1.對(duì)char褒傅,byte或者short進(jìn)行移位處理弃锐,那么在移位進(jìn)行之前,它們會(huì)自動(dòng)轉(zhuǎn)換成一個(gè)int殿托。只有右側(cè)的5個(gè)低位才會(huì)有用霹菊。這樣可防止我們?cè)谝粋€(gè)int數(shù)里移動(dòng)不切實(shí)際的位數(shù)。若對(duì)一個(gè)long值進(jìn)行處理支竹,最后得到的結(jié)果也是long旋廷。此時(shí)只會(huì)用到右側(cè)的6個(gè)低位,防止移動(dòng)超過(guò)long值里現(xiàn)成的位數(shù)礼搁。
2.移位操作符能操作的數(shù)只有int類型和long類型饶碘,這個(gè)是指左操作數(shù)的類型。對(duì)于int類型而言馒吴,int在Java中占4字節(jié)扎运,一共32位瑟曲,也就是說(shuō),對(duì)于一個(gè)在Java中的int數(shù)據(jù)豪治,做32次移位洞拨,那么這個(gè)int數(shù)據(jù)就完全變了,以左移為例鬼吵,左移是補(bǔ)0扣甲,那么對(duì)于任意一個(gè)int類型數(shù)據(jù)篮赢,做32次移位齿椅,那么int數(shù)據(jù)變成32位全0的數(shù)據(jù),Java不允許一次性移位左操作數(shù)的所有位启泣,也就是右操作數(shù)不能大于32涣脚。于是回到上述的句子,其指的是右操作數(shù)的低5位寥茫,5位二進(jìn)制所代表的最大值為2^5-1遣蚀,為31,所以取右操作數(shù)的低5位纱耻,就是只看右操作數(shù)的二進(jìn)制的低5位芭梯,其數(shù)值不會(huì)超過(guò)2^5次方,也就是int的32位弄喘。因此玖喘,移位操作符進(jìn)行移位的實(shí)際次數(shù),其實(shí)是右操作數(shù)2的次數(shù)
3.移位操作符操作的運(yùn)算對(duì)象是二進(jìn)制的“位”蘑志,int類型是32位也就是2的5次冪 累奈!如果移32位以上,那么原來(lái)的數(shù)的信息會(huì)全部丟失急但,這樣也就沒(méi)有什么意義了澎媒!所以上面的“只有右側(cè)的5個(gè)低位才會(huì)有用”說(shuō)的是:移位操作符右端的那個(gè)數(shù)(化成二進(jìn)制)的低5位才有用,即
X < <y;
是指y的低5位才有用波桩,即不能大于32戒努。 而對(duì)于long型也是同樣的道理!
因此镐躲,如果對(duì)一個(gè)int 型储玫,進(jìn)行移位,X < <y; 當(dāng)y小于32時(shí)匀油,移位后的結(jié)果一般都在我們的預(yù)料當(dāng)中缘缚;而如果y大于32時(shí),由于移位超出了int所能表示的范圍敌蚜,這時(shí)就先把y化成二進(jìn)制數(shù)桥滨,然后取該二進(jìn)制數(shù)右端的低5位,再把這5位化成十進(jìn)制,此時(shí)的這個(gè)十進(jìn)制就是要對(duì)X移動(dòng)的位數(shù)齐媒。
java中二進(jìn)制轉(zhuǎn)十進(jìn)制:Integer.toBinaryString(34) //100010十進(jìn)制轉(zhuǎn)二進(jìn)制:Integer.valueOf("00010",2) //2
ps:轉(zhuǎn)發(fā)自?https://www.cnblogs.com/winsker/p/6728672.html