<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:說下Golang Slice的底層實(shí)現(xiàn),淚崩了!

          共 598字,需瀏覽 2分鐘

           ·

          2022-03-11 06:12


          1、Go 語言當(dāng)中數(shù)組和切片的區(qū)別是什么?


          數(shù)組:

          數(shù)組固定長(zhǎng)度數(shù)組長(zhǎng)度是數(shù)組類型的一部分,所以[3]int 和[4]int 是兩種不同 的數(shù)組類型數(shù)組需要指定大小,不指定也會(huì)根據(jù)處初始化對(duì)的自動(dòng)推算出大 小,不可改變數(shù)組是通過值傳遞的


          切片: 切片可以改變長(zhǎng)度切片是輕量級(jí)的數(shù)據(jù)結(jié)構(gòu),三個(gè)屬性,指針,長(zhǎng)度,容量不 需要指定大小切片是地址傳遞(引用傳遞)可以通過數(shù)組來初始化,也可以通 過內(nèi)置函數(shù) make()來初始化,初始化的時(shí)候 len=cap,然后進(jìn)行擴(kuò)容。


          2、Go 語言當(dāng)中值傳遞和地址傳遞(引用傳遞)如何運(yùn)用?有什么區(qū)別?

          1. ?值傳遞只會(huì)把參數(shù)的值復(fù)制一份放進(jìn)對(duì)應(yīng)的函數(shù),兩個(gè)變量的地址不同, 不可相互修改。

          2. 地址傳遞(引用傳遞)會(huì)將變量本身傳入對(duì)應(yīng)的函數(shù),在函數(shù)中可以對(duì)該變 量進(jìn)行值內(nèi)容的修改。


          3、Go 語言當(dāng)中數(shù)組和切片在傳遞的時(shí)候的區(qū)別是什么?

          • 數(shù)組是值傳遞

          • 切片是引用傳遞


          4、Go 語言是如何實(shí)現(xiàn)切片擴(kuò)容的?
          func?main()?{
          ????arr?:=?make([]int,?0)
          ????for?i?:=?0;?i?2000;?i++?{
          fmt.Println("len?為",?len(arr),?"cap?為",?cap(arr))

          ????????arr?=?append(arr,?i)
          ????}
          }


          我們可以看下結(jié)果

          依次是0,1,2,4,8,16,32,64,128,256,512,1024

          但到了 1024 之后,就變成了1024,1280,1696,2304

          每次都是擴(kuò)容了四分之一左右

          5、看下面代碼的 defer 的執(zhí)行順序是什么? defer 的作用和特點(diǎn)是什么?

          defer 的作用是:
          你只需要在調(diào)用普通函數(shù)或方法前加上關(guān)鍵字 defer,就完成了 defer 所需要 的語法。當(dāng) defer 語句被執(zhí)行時(shí),跟在 defer 后面的函數(shù)會(huì)被延遲執(zhí)行。直到 包含該 defer 語句的函數(shù)執(zhí)行完畢時(shí),defer 后的函數(shù)才會(huì)被執(zhí)行,不論包含defer 語句的函數(shù)是通過 return 正常結(jié)束,還是由于 panic 導(dǎo)致的異常結(jié)束。你可以在一個(gè)函數(shù)中執(zhí)行多條 defer 語句,它們的執(zhí)行順序與聲明順序相反。


          defer 的常用場(chǎng)景:


          • defer語句經(jīng)常被用于處理成對(duì)的操作,如打開、關(guān)閉、連接、斷開連接、加鎖、釋放鎖。

          • 通過defer機(jī)制,不論函數(shù)邏輯多復(fù)雜,都能保證在任何執(zhí)行路徑下,資

            源被釋放。

          • 釋放資源的defer應(yīng)該直接跟在請(qǐng)求資源的語句后。

          6、Golang Slice 的底層實(shí)現(xiàn)

          切片是基于數(shù)組實(shí)現(xiàn)的,它的底層是數(shù)組,它自己本身非常小,可以理解為對(duì)底層數(shù)組的抽象。因?yàn)榛跀?shù)組實(shí)現(xiàn),所以它的底層的內(nèi)存是連續(xù)分配的,效率非常高,還可以通過索引獲得數(shù)據(jù),可以迭代以及垃圾回收優(yōu)化。


          切片本身并不是動(dòng)態(tài)數(shù)組或者數(shù)組指針。它內(nèi)部實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)通過指針引用 底層數(shù)組,設(shè)定相關(guān)屬性將數(shù)據(jù)讀寫操作限定在指定的區(qū)域內(nèi)。切片本身是一 個(gè)只讀對(duì)象,其工作機(jī)制類似數(shù)組指針的一種封裝。


          切片對(duì)象非常小,是因?yàn)樗侵挥?3 個(gè)字段的數(shù)據(jù)結(jié)構(gòu):

          • 指向底層數(shù)組的指針

          • 切片的長(zhǎng)度

          • 切片的容量

          7、Golang Slice 的擴(kuò)容機(jī)制,有什么注意點(diǎn)?

          Go 中切片擴(kuò)容的策略是這樣的:


          • 首先判斷,如果新申請(qǐng)容量大于2倍的舊容量,最終容量就是新申請(qǐng)的容 量

          • 否則判斷,如果舊切片的長(zhǎng)度小于1024,則最終容量就是舊容量的兩倍

          • 否則判斷,如果舊切片長(zhǎng)度大于等于1024,則最終容量從舊容量開始循環(huán)

            增加原來的 1/4, 直到最終容量大于等于新申請(qǐng)的容量

          • 如果最終容量計(jì)算值溢出,則最終容量就是新申請(qǐng)容量

          8、擴(kuò)容前后的 Slice 是否相同?

          情況一:

          原數(shù)組還有容量可以擴(kuò)容(實(shí)際容量沒有填充完),這種情況下,擴(kuò)容以后的 數(shù)組還是指向原來的數(shù)組,對(duì)一個(gè)切片的操作可能影響多個(gè)指針指向相同地址 的 Slice


          情況二:

          原來數(shù)組的容量已經(jīng)達(dá)到了最大值,再想擴(kuò)容, Go 默認(rèn)會(huì)先開一片內(nèi)存區(qū) 域,把原來的值拷貝過來,然后再執(zhí)行 append() 操作。這種情況絲毫不影響 原數(shù)組。
          要復(fù)制一個(gè)
          Slice,最好使用 Copy 函數(shù)。

          9、Golang 的參數(shù)傳遞、引用類型

          Go 語言中所有的傳參都是值傳遞(傳值),都是一個(gè)副本,一個(gè)拷貝。因?yàn)榭?貝的內(nèi)容有時(shí)候是非引用類型(int、string、struct 等這些),這樣就在函 數(shù)中就無法修改原內(nèi)容數(shù)據(jù);有的是引用類型(指針、map、slice、chan等 這些),這樣就可以修改原內(nèi)容數(shù)據(jù)。

          Golang 的引用類型包括 slice、map 和 channel。它們有復(fù)雜的內(nèi)部結(jié)構(gòu),除 了申請(qǐng)內(nèi)存外,還需要初始化相關(guān)屬性。內(nèi)置函數(shù) new 計(jì)算類型大小,為其分 配零值內(nèi)存,返回指針。而 make 會(huì)被編譯器翻譯成具體的創(chuàng)建函數(shù),由其分 配內(nèi)存和初始化成員結(jié)構(gòu),返回對(duì)象而非指針。

          10、Golang Map 底層實(shí)現(xiàn)

          Golang 中 map 的底層實(shí)現(xiàn)是一個(gè)散列表,因此實(shí)現(xiàn) map 的過程實(shí)際上就是實(shí)現(xiàn) 散表的過程。在這個(gè)散列表中,主要出現(xiàn)的結(jié)構(gòu)體有兩個(gè),一個(gè)叫 hmap(a header for a go map),一個(gè)叫 bmap(a bucket for a Go map,通常叫其bucket)。

          11、Golang Map 如何擴(kuò)容

          裝載因子:count/2^B
          觸發(fā)條件:
          1. 裝填因子是否大于6.5
          2. overflow bucket 是否太多
          解決方法:
          1.
          雙倍擴(kuò)容:擴(kuò)容采取了一種稱為“漸進(jìn)式”地方式,原有的 key 并不會(huì)一

          次性搬遷完畢,每次最多只會(huì)搬遷 2 個(gè) bucket
          2.
          等量擴(kuò)容:重新排列,極端情況下,重新排列也解決不了,map成了鏈表,

          性能大大降低,此時(shí)哈希種子 hash0 的設(shè)置,可以降低此類極端場(chǎng)景的發(fā) 生。


          12、Golang Map 查找

          Go 語言中 map 采用的是哈希查找表,由一個(gè) key 通過哈希函數(shù)得到哈希值,64位系統(tǒng)中就生成一個(gè) 64bit 的哈希值,由這個(gè)哈希值將 key 對(duì)應(yīng)到不同的桶 (bucket)中,當(dāng)有多個(gè)哈希映射到相同的的桶中時(shí),使用鏈表解決哈希沖 突。key 經(jīng)過 hash 后共 64 位,根據(jù) hmap 中 B 的值,計(jì)算它到底要落在哪個(gè)桶 時(shí),桶的數(shù)量為 2^B,如 B=5,那么用 64 位最后 5 位表示第幾號(hào)桶,在用 hash值的高 8 位確定在 bucket 中的存儲(chǔ)位置,當(dāng)前 bmap 中的 bucket 未找到,則查 詢對(duì)應(yīng)的 overflow bucket,對(duì)應(yīng)位置有數(shù)據(jù)則對(duì)比完整的哈希值,確定是否 是要查找的數(shù)據(jù)。

          如果兩個(gè)不同的 key 落在的同一個(gè)桶上,hash 沖突使用鏈表法接近,遍歷bucket 中的 key 如果當(dāng)前處于 map 進(jìn)行了擴(kuò)容,處于數(shù)據(jù)搬移狀態(tài),則優(yōu)先從oldbuckets 查找。

          13、介紹一下 Channel

          Go 語言中,不要通過共享內(nèi)存來通信,而要通過通信來實(shí)現(xiàn)內(nèi)存共享。Go 的CSP(Communicating Sequential Process)并發(fā)模型,中文可以叫做通信順序進(jìn) 程,是通過 goroutine 和 channel 來實(shí)現(xiàn)的。
          所以 channel 收發(fā)遵循先進(jìn)先出 FIFO,分為有緩存和無緩存,channel 中大致 有 buffer(當(dāng)緩沖區(qū)大小部位 0 時(shí),是個(gè) ring buffer)、sendx 和 recvx 收發(fā) 的位置(ring buffer 記錄實(shí)現(xiàn))、sendq、recvq 當(dāng)前 channel 因?yàn)榫彌_區(qū)不足 而阻塞的隊(duì)列、使用雙向鏈表存儲(chǔ)、還有一個(gè) mutex 鎖控制并發(fā)、其他原屬 等。

          14、Go 語言的 Channel 特性?

          1. 給一個(gè) nil channel 發(fā)送數(shù)據(jù),造成永遠(yuǎn)阻塞

          2. 從一個(gè) nil channel 接收數(shù)據(jù),造成永遠(yuǎn)阻塞

          3. 給一個(gè)已經(jīng)關(guān)閉的 channel 發(fā)送數(shù)據(jù),引起 panic

          4. 從一個(gè)已經(jīng)關(guān)閉的 channel 接收數(shù)據(jù),如果緩沖區(qū)中為空,則返回一個(gè)零

          5. 無緩沖的channel是同步的,而有緩沖的channel是非同步的

          6. 關(guān)閉一個(gè) nil channel 將會(huì)發(fā)生 panic


          15、Channel 的 ring buffer 實(shí)現(xiàn)

          channel 中使用了 ring buffer(環(huán)形緩沖區(qū)) 來緩存寫入的數(shù)據(jù)。ring buffer 有很多好處,而且非常適合用來實(shí)現(xiàn) FIFO 式的固定長(zhǎng)度隊(duì)列。在 channel 中,ring buffer 的實(shí)現(xiàn)如下:

          hchan 中有兩個(gè)與 buffer 相關(guān)的變量:recvx 和 sendx。其中 sendx 表示buffer 中可寫的 index,recvx 表示 buffer 中可讀的 index。從 recvx 到sendx 之間的元素,表示已正常存放入 buffer 中的數(shù)據(jù)。
          我們可以直接使用 buf[recvx]來讀取到隊(duì)列的第一個(gè)元素,使用 buf[sendx] = x 來將元素放到隊(duì)尾。


          最后:

          點(diǎn)擊下方名片鏈接,關(guān)注 「碼農(nóng)編程進(jìn)階筆記?」微信公眾號(hào),在微信聊天對(duì)話框回復(fù)「go電子書」「黑馬Go語言」或者直接長(zhǎng)按左下圖海報(bào)中的二維碼,可獲取最新golang電子書和視頻資源


          瀏覽 47
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青草青草青青草 | 色婷婷在线视频精品导航 | 操逼操| 欧美91成人网 | 99视频免费在线 |