不知道上一篇文章看的怎麼樣了:害怕面試被問HashMap?這一篇就搞定了!
在這篇文章中,我比較詳細的分析了爲什麼HashMap的初始化容量是16以及爲什麼容量的大小要是2的整數次冪!
不知道你看懂了沒,如果你看的懵懵懂懂的話,我猜你對以下基礎知識一定不那麼熟悉:
- java中的位運算
- 進制之間的轉換
- 原碼,反碼和補碼
怎麼樣,對這些基礎知識掌握的如何,這些可以說都是大學時候學的計算機基礎了,不過,我知道你當時肯定沒學會,即使學會了也忘得差不多了。
其實吧,這些基礎知識真的超級重要,你看,現在閱讀源碼的時候因爲這些基礎知識不過關而遇到坎了吧。
別擔心,今天咱們一起越過這道坎!
先從進制轉換開始
對於進制轉換這個啊,說來慚愧,我之前學過不止一次,曾經有一次還花了很長時間,做筆記,畫圖,弄了滿滿的一張A4紙,當時覺得對進制轉換這塊完全OK了,以後再也不怕進制轉換了。
可是嘞後來讀源碼的時候遇到進制轉換的時候還是覺得不知所措,發現之前學的都忘得差不多了,唉。
所以啊,對於學習,我們可不能一味的向前學習新知識,對於之前的知識也要經常回顧,溫故而知新,可以爲師矣嘛
好啦,咱們這次再來一起學習下進制轉換吧!
對於進制轉換啊,其實我們的重點主要放在與十進制之間的各種轉換即可,因爲這纔是我們平常使用頻率比較高的,所以要優先熟練的掌握這些。
啥是二進制
十進制我們再熟悉不過了,那啥是二進制嘞,簡單來說,二進制就是用0和1來表示的數值,在十進制中逢十進一,借一當十,而二進制呢?那就是逢二進一,借一當二,這個好懂,但是這裏我要說些概念。
什麼嘞?後面我們要說到原碼,反碼和補碼,這裏先說下,它們都是二進制數,也就是都是由0和1表示的,單是可能具體的數值不一樣。
我們要知道,對於計算機而言,它只認識0和1,所以對於數據在計算機中的存儲都是以二進制的形式來存儲的,好了,先有這個概念。
十進制與二進制之間的一個規律
先來看一個圖:
不知道你看出來什麼沒,主要看標紅的,前面說過了,二進制就是逢二進一,那麼上圖中標紅的二進制是不是都是產生進位的數值,啥意思嘞,比如二進制11,並沒有產生進位,如果要產生進位,那就要再加一,然後就會產生進位得100,你看是不是?
再看進位產生的二進制對應的十進制是不是這些數值:
2,4,8,16……
那麼你看這些產生進位的二進制數值有什麼特點,是不是最高位(最左邊)是不是都是1,其他都是0,所以這裏就有個規律:
十進制下的2的整數次冪的數的二進制的高位都是1,其他全是0,然後看是2的幾次冪,是幾就有幾個零
好吧,我相信這裏沒什麼難理解的!
ps:看到這裏你是不是更容易理解爲什麼HashMap的容量要是2的整數次冪了呢?
二進制轉十進制
那麼二進制的數據怎麼轉換成十進制呢?我們看十進制的11從右往左是不是依次代表1個1,1個10,加起來也就是11.
那麼來看二進制1011怎麼表示,看下面的:
二進制的數從右向左各個位表示十進制的含義:
第一個1表示:1的個數(有1個1)
第二個1表示:2的個數(有1個2)
第三個0表示:4的個數(有0個4)
第四個1表示:8的個數(有1個8)
爲什麼是1(默認右邊第一位開始),2,4,8呢?注意上面說的那個規律,所以上面的加起來就是1+2+0+8等於11。
怎麼樣?二進制轉十進制是不是比較簡單,其實後面還有個公式,等下再說,我們繼續往下看
十進制轉二進制
知道嗎?這裏有個通用的方法那就是:除以2,餘數逆序排列,看個例子吧:
也就是說十進制轉換成二進制的話,那就用十進制的數除以2,然後取餘數之後將餘數逆序排列就是對應的二進制數了,怎麼樣是不是更簡單,我們接着往下看
十進制與其他進制之間的轉換
我們之前就說過,對於進制轉換我們重點關注的就是與十進制之間的轉換,我們上面介紹了二進制和十進制之間的互相轉換,那麼還有八進制和十六進制,它們與十進制之間的轉換又是怎樣呢?
這裏首先記住十進制轉換其他進制的通用方法:
“除基數B取餘,逆序排列”方法可以將十進制數轉換爲任意進制數。
那麼這個基數B是啥呢?比如十進制轉換成二進制,那麼這個基數B就是2,知道了吧!
那麼其他進制轉換成十進制怎麼整嘞?記住這個公式:
這個公式可以概括爲:“按權展開”—其他進制轉換爲十進制(B表示各進制的基數,n表示位數)
舉個例子,比如之前1011這個二進制轉換成十進制就可以這樣:
就是這樣的啦,至於其他的什麼八進制和十六進制之間的互相轉換什麼的,不怎麼常用,感興趣的自行了解,對了,我平常都是直接搜索在線進制轉換的😂
進攻位運算
瞭解了進制轉換,尤其是十進制和二進制之間的轉換之後我們就可以開始學習java中的位運算了,因爲位運算實際上都是對二進制進行操作的,接下來我將逐步分析java中常見的位運算。
左移 <<
舉個栗子,5 << 2 將5左移2 結果爲20 爲啥?注意是對二進制進行的操作,來看:
首先會將5轉爲二進制表示形式(java中,整數默認就是int類型,也就是32位):
怎麼去理解,首先在Java中,整數默認就是int類型,也就是佔4個字節32位,你就可以想成這樣
0000 0000 0000 0000 0000 0000 0000 0000
這就是32位,但是每位上都是0,這是一個標準,用於後面的比較,比如5的二進制是
0000 0000 0000 0000 0000 0000 0000 0101
然後把它與標準的進行對比,也就是這樣:
這時候的區別就在後四位,然後將5左移(<<)2,也就是5的二進制以標準爲參考整體左移2位,也就是這樣:
這樣一來,就產生了錯位,看圖:
不過這時候看着總是有點彆扭,應該都是四位四位的在一塊吧,所以從低位開始,四位一組,就成了這樣
0000 0000 0000 0000 0000 0000 0001 0100
換算成十進制就是20了 這就是5 << 2得到結果的由來。
右移 >>
還是先將5轉爲2進製表示形式:
0000 0000 0000 0000 0000 0000 0000 0101 然後右移2位,高位補0:
0000 0000 0000 0000 0000 0000 0000 0001
看圖:
仔細看圖分析分析😃
無符號右移 >>>
先記住這句話:正數右移,高位補0,負數右移,高位補1,負數無符號右移,高位補0 正數無符號右移 ,高位補0 正數換算成二進制後的最高位爲0,負數的二進制最高位爲1
接下來依然是看例子,在此之前我看過好多別人寫的,發現好多都喜歡用5舉例子你知道爲啥嗎😂
5換算成二進制是: 0000 0000 0000 0000 0000 0000 0000 0101
5右移3位後結果爲0,0的二進制爲: 0000 0000 0000 0000 0000 0000 0000 0000 // (這裏高位補0)
-5換算成二進制是: 1111 1111 1111 1111 1111 1111 1111 1011
-5右移3位後結果爲-1,-1的二進制爲: 1111 1111 1111 1111 1111 1111 1111 1111 // (高位補1)
-5無符號右移3位後的結果 536870911 換算成二進制: 0001 1111 1111 1111 1111 1111 1111 1111 // (高位補0)
這裏需要注意了:以上說的都是右移的情況,如果是左移,無論是正數還是負數,低位都是用0補
時間關係,就不贅述了。
位與 &
其實核心都是二進制,所以掌握好進制轉換是關鍵,看看位與 &是怎麼計算的:
這裏要看兩個操作數的二進制的各個位的對應情況,總結起來也就是:
有0則0,否則爲1
什麼意思呢?我們舉一個例子來看看
我們猜這個結果是什麼,答案是
注意這裏可不是6除以3,它是這樣計算的,首先6和3都要轉成二進制,6的二進制是110,3的二進制是11也就是011,那麼這樣運算
我相信看圖就能明白的,而二進制的010就是2啦,這就是位與的操作,對於或和亦或其實同樣道理,我們繼續來看:
位或 |
規則就是:有1則1
看例子:
位異或 ^
規則是:相同則0,不同則1
看例子:
有點難懂的位非 ~
這是網站隨便找的一個例子,你看看,求~5,得-6,爲啥?
它這裏的規則是:操作數的第n位爲1,那麼結果的第n位爲0,反之。
不知道你懂嗎?有這麼一個解釋:
6的二進制數爲: 0000 0000 0000 0000 0000 0000 0000 0110 然後6的二進制反碼爲: 1111 1111 1111 1111 1111 1111 1111 1001 將反碼+1得到-6的補碼二進制數:1111 1111 1111 1111 1111 1111 1111 1010
所以這裏就牽涉到原碼,反碼和補碼了,另外還有個重點的點就是:
負數在計算機中是以補碼的形式存在的
好了,我們趕緊來看看什麼是原碼,反碼和補碼吧!
最後攻克原碼,反碼和補碼
這是非常重要的概念,需要熟練掌握,要記得。開始之前,先上一個重要的結論:
數據在計算機中的存儲是二進制的形式,二進制簡單來說就是0和1組合的,無論原碼,反碼還是補碼,都是二進制的形式
其次我們要注意的點就是正數和負數的原碼,反碼和補碼,是有區別的。
原碼
什麼是原碼嘞?
對於正數來說,我們把它的絕對值轉換成的二進制數叫做正數的原碼,對於負數來說我們把它的絕對值轉換成的二進制數,然後最高位補1,稱爲原碼。看看,還是有區別的。
比如 :
00000000 00000000 00000000 00000101 是 5的 原碼。
10000000 00000000 00000000 00000101 是 -5的 原碼。
這裏有個點就是最高位是0代表正數,是1代表負數。
正數和負數在計算機中怎麼表示的
接下來就是在計算機中的表示:
對於正數:原碼,反碼和補碼都是一樣的,所以在計算機中怎麼說都一樣,反正就是本身轉換成二進制的結果,可以說原碼,也可以說補碼,因爲都是一樣的
對於負數:原碼,反碼和補碼是不一樣的,在計算機中***負數是以補碼的形式存在的***
所以說,重點聚焦在負數上,看它的反碼和補碼是怎麼表示的
反碼
那反碼是啥嘞?
負數的反碼爲對該數的原碼除符號位外各位取反[每一位取反(除符號位)]。
這裏有個符號位,啥?還記得剛剛說的嗎?
最高位是0代表正數,是1代表負數。
也就是最左邊的那一位。
取反操作指:原爲1,得0;原爲0,得1。(1變0; 0變1)
比如:
正數00000000 00000000 00000000 00000101 的反碼還是
00000000 00000000 00000000 00000101
負數10000000 00000000 00000000 00000101 的反碼則是
11111111 11111111 11111111 11111010
另外啊反碼是相互的,因此也可稱:
10000000 00000000 00000000 00000101 和 11111111 11111111 11111111 11111010互爲反碼。
不過這裏有個問題,那就是+0和-0,什麼意思呢?
原碼和反碼在表示數的時候的有點尷尬啊,比如表示零的時候,同樣都是0,但是原碼就有兩種表示法:
[-0]原=10000000
[+0]原=00000000
反碼也有兩種表示法:
[+0]反=00000000
[- 0]反=11111111
這就有點難受啊,不都是0嗎,於是乎,就出了補碼
補碼
那啥又是補碼嘞?
負數的補碼爲對該數的原碼除符號位外各位取反,然後在最後一位加1
比如:10000000 00000000 00000000 00000101 的反碼是:11111111 11111111 11111111 11111010
那麼,補碼爲:
11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011
這裏有兩點需要注意:
1、從補碼求原碼的方法跟原碼求補碼是一樣的 ,也可以通過完全逆運算來做,先減一,再取反。
2、補碼卻規定0沒有正負之分
第二個啥意思呢,也就是說對於0的表示,補碼只有一種[-0]補=00000000。感覺這樣就正常多了。
總結一下
原碼錶示法規定:用符號位(最左邊)和數值表示帶符號數,正數的符號位用“0”表示,負數的符號位用“1”表示,數值部分用二進制形式表示。
反碼錶示法規定:正數的反碼與原碼相同,負數的反碼爲對該數的原碼除符號位外各位取反。
補碼錶示法規定:正數的補碼與原碼相同,負數的補碼爲對該數的原碼除符號位外各位取反,然後在最後一位加1.
正零和負零的補碼相同,[+0]補=[-0]補=0000 0000B
簡單說說
其實你看,這些基礎知識仔細研究起來,還是有一定難度的,覺得亂巴巴的,怪不得上大學的時候聽不懂呢😂,而且這三個知識點貌似經常一起運用,所以對於這三點基礎,還是有必要花時間琢磨一番的。
好啦,就到這了,歡迎大家一起討論!
感謝閱讀
大學的時候選擇了自學Java,工作了發現吃了計算機基礎不好的虧,學歷不行這是沒辦法的事,只能後天彌補,於是在編碼之外開啓了自己的逆襲之路,不斷的學習Java核心知識,深入的研習計算機基礎知識,所有心得全部書寫成文,整理成有目錄的PDF,持續原創,PDF在公衆號持續更新,如果你也不甘平庸,那就與我一起在編碼之外,不斷成長吧!
其實這裏不僅有技術,更有那些技術之外的東西,比如,如何做一個精緻的程序員,而不是“屌絲”,程序員本身就是高貴的一種存在啊,難道不是嗎?
非常歡迎你的加入,未來的日子,編碼之外,有你有我,一起做一個人不傻,錢很多,活得久的快樂的程序員吧!
回覆關鍵字“PDF”,獲取技術文章合集,已整理好,帶有目錄,歡迎一起交流技術!
另外回覆“慶哥”,看慶哥給你準備的驚喜大禮包,只給首次關注的你哦!
任何問題,可以加慶哥微信:H653836923,另外,我有個交流羣,我會***不定期在羣裏分享學習資源,不定時福利***,感興趣的可以說下我邀請你!
對了,如果你是個Java小白的話,也可以加我微信,我相信你在學習的過程中一定遇到不少問題,或許我可以幫助你,畢竟我也是過來人了!
感謝各位大大的閱讀🥰