您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(guān)Java中位運(yùn)算及常見進(jìn)制的示例分析的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧。
二進(jìn)制(Binary)
數(shù)值范圍0,1,滿2進(jìn)1
以0b或0B開頭
bit比特是計(jì)算機(jī)最小存儲(chǔ)單元,1個(gè)bit占用1個(gè)二進(jìn)制位即0或1
1個(gè)byte字節(jié)有8個(gè)bit即占用8個(gè)二進(jìn)制位
int整型4字節(jié)占用32個(gè)二進(jìn)制位
二進(jìn)制左半部分表示高位,右半部分為低位
二進(jìn)制最高位為0表示正數(shù),最高位為1表示負(fù)數(shù)
二進(jìn)制原碼取反得到反碼,反碼補(bǔ)1得到補(bǔ)碼,負(fù)數(shù)使用補(bǔ)碼表示
八進(jìn)制(Octal)
數(shù)值范圍0-7,滿8進(jìn)1
以數(shù)字0開頭表示
十進(jìn)制(Decimal)
數(shù)值范圍0-9 ,滿10進(jìn)1
日常阿拉伯?dāng)?shù)字即十進(jìn)制
十六進(jìn)制(Hexadecimal)
數(shù)值范圍0-9及A-F,滿16進(jìn)1
以0x或0X開頭表示。 此處的A-F不區(qū)分大小寫
按位與(&)
都為1則得1
按位或(|)
有一個(gè)為1即得1
按位異或(^)
不同得1,相同得0
按位取反(~)
取反即1變0、0變1
按位左移(<<)
按位左移幾位,高位會(huì)被截掉幾位,正負(fù)數(shù),低位都會(huì)被補(bǔ)幾個(gè)0
按位右移(>>)
按位右移幾位,低位就會(huì)被截掉幾位,正數(shù)高位會(huì)被補(bǔ)幾個(gè)0,負(fù)數(shù)高位會(huì)被補(bǔ)幾個(gè)1
按位無(wú)符號(hào)右移(>>>)
按位右移幾位,低位就會(huì)被截掉幾位,正負(fù)數(shù)數(shù)高位會(huì)被補(bǔ)幾個(gè)0
按位無(wú)符號(hào)左移(<<<)
按位左移幾位,高位就會(huì)被截掉幾位,正負(fù)數(shù)數(shù)低位都會(huì)被補(bǔ)幾個(gè)0
HashMap底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組+鏈表
,通過(guò)put(K key, V value)
方法添加元素,底層四步曲如下:
第一步曲:根據(jù)key得到hashCode值
第二步曲:根據(jù)hashCode值計(jì)算出hash值
第三步曲:根據(jù)hash值計(jì)算出元素(key/value)最終要放在哪個(gè)數(shù)組index下標(biāo)
第四步曲:最后根據(jù)元素(key/value)新建節(jié)點(diǎn)并保存到指定數(shù)組index下標(biāo)位置
Java HashMap添加元素的示例代碼:
HashMap<Object, Object> map = new HashMap<>(); map.put("name","Justin");
HashMap底層put(key,value)方法源碼:
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
接下來(lái)將解讀底層源碼用到哪些位運(yùn)算,有什么奧妙之處
根據(jù)key得到hashCode值
可以看到hash值計(jì)算的過(guò)程就用到了^
(異或)和>>>
(無(wú)符號(hào)右移)兩種位運(yùn)算
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
這里key是字符串"name",String重寫了計(jì)算字符串hashCode值的hashCode()方法,源碼如下:
計(jì)算得到hashCode值為3373707
根據(jù)hashCode值計(jì)算出hash值(h = key.hashCode()) ^ (h >>> 16)
即 (3373707) ^ (3373707 >>> 16)
3373707
二進(jìn)制表達(dá)0000000001100110111101010001011
h >>> 16
二進(jìn)制表達(dá)00000000000000000000000000110011
根據(jù)^
異或運(yùn)算原理即不同得1,相同得0
得到3373707 ^ (3373707 >>>16)
二進(jìn)制結(jié)果為:0000000001100110111101010111000
進(jìn)制在線轉(zhuǎn)換:http://tools.jb51.net/transcoding/hexconvert
即計(jì)算key的hash值得到3373752
,斷點(diǎn)往后查看hash值剛好也是這個(gè)值
根據(jù)hash值計(jì)算出元素(key/value)最終要放在哪個(gè)數(shù)組index下標(biāo)
公式:i = (n - 1) & hash
這里就用到了&
按位與運(yùn)算(都為1則得1)
公式(n - 1) & hash
的奧妙之處在于,n
表示HashMap中的數(shù)組容量大小,并且剛好是16,32,64…2的次方,這種情況其實(shí)是等效于 hash % n
取模,計(jì)算出的數(shù)組index下標(biāo)值一樣,還能夠保證不會(huì)數(shù)組下標(biāo)越界
但是HashMap這里沒(méi)有使用%
取模,因?yàn)閔ash值是int整型即十進(jìn)制數(shù)值,使用%取模會(huì)先將內(nèi)存數(shù)據(jù)轉(zhuǎn)成十進(jìn)制再進(jìn)行運(yùn)算,多了這部分的性能開銷,因此效率比較低
HashTable底層倒是用的%取模,hash值與十六進(jìn)制0x7FFFFFFF
做按位與運(yùn)算目的是為了保證hash值始終是正數(shù)
有的小伙伴可能會(huì)問(wèn)了,使用%取模計(jì)算,那這里為啥HashTable還在用,我想說(shuō)的是其實(shí)也可以優(yōu)化,只不過(guò)HashTable本身就是主打synchronized線程安全,也就不考慮優(yōu)化%取模為位運(yùn)算了
最后根據(jù)元素(key/value)新建節(jié)點(diǎn)并保存到指定數(shù)組index下標(biāo)位置
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) { return new Node<>(hash, key, value, next); }
關(guān)于位運(yùn)算的使用,文中在介紹第三步曲時(shí),也提到了HashMap計(jì)算數(shù)組下標(biāo)使用%取模和位運(yùn)算的問(wèn)題,使用于位運(yùn)算的奧妙之處在直接從內(nèi)存讀取數(shù)據(jù)進(jìn)行計(jì)算,不需要轉(zhuǎn)成十進(jìn)制,如果使用%取模需要先轉(zhuǎn)成十進(jìn)制,有性能開銷,效率比較低
HashMap底層除了文中提到的^
按位異或、>>>
無(wú)符號(hào)右移、&
按位與位運(yùn)算,其實(shí)在HashMap的擴(kuò)容機(jī)制resize()
中,還用到了<<
左移運(yùn)算oldCap << 1
這里oldCap << 1
剛好是兩倍,可以總結(jié)的說(shuō)一個(gè)數(shù)與n
進(jìn)行左移運(yùn)算,結(jié)果為這個(gè)數(shù)乘以2的n次方oldCap << 1
等值 oldCap = oldCap * (2的n次方)
同理,一個(gè)數(shù)與n
進(jìn)行右移運(yùn)算結(jié)果為這個(gè)數(shù)除以2的n次方oldCap >> 1
等值 oldCap = oldCap / (2的n次方)
感謝各位的閱讀!關(guān)于“Java中位運(yùn)算及常見進(jìn)制的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。