欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語(yǔ)言常見(jiàn)數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)詳解

 更新時(shí)間:2023年07月27日 11:33:31   作者:葉楓樺  
這篇文章主要為大家學(xué)習(xí)介紹了Go語(yǔ)言中的常見(jiàn)數(shù)據(jù)結(jié)構(gòu)(channal、slice和map)的實(shí)現(xiàn),文中的示例代碼簡(jiǎn)潔易懂,需要的可以參考一下

channal

channal是go中的管道,主要用于協(xié)程之間的通信,他有點(diǎn)類似于阻塞隊(duì)列,使用管道可以簡(jiǎn)單的實(shí)現(xiàn)生產(chǎn)者消費(fèi)者,他會(huì)幫助我們自動(dòng)的去阻塞或者喚醒groutine

創(chuàng)建寫入和寫出

c := make(chan int, 5)
c <- 1
v := <-c

channal中如果是nil的話讀取和寫入都不會(huì)觸發(fā)panic并且阻塞groutine如果是關(guān)閉的channal的話是可以讀取的但是不能寫如果寫的話就會(huì)觸發(fā)panic

channal的源碼在runtime/chan.go中下面是結(jié)構(gòu)體

type hchan struct {
   qcount   uint           // total data in the queue
   dataqsiz uint           // size of the circular queue
   buf      unsafe.Pointer // points to an array of dataqsiz elements
   elemsize uint16
   closed   uint32
   elemtype *_type // element type
   sendx    uint   // send index
   recvx    uint   // receive index
   recvq    waitq  // list of recv waiters
   sendq    waitq  // list of send waiters
}

根據(jù)結(jié)構(gòu)體我們也不難發(fā)現(xiàn)它的數(shù)據(jù)結(jié)構(gòu)其實(shí)就是一個(gè)循環(huán)隊(duì)列,同時(shí)又有兩個(gè)recvq和sendq去代表寫操作和讀操作的阻塞隊(duì)列,qcount表示當(dāng)前使用大小也就是len(),dataqsiz表示容積大小也就是cap(),buf表示真實(shí)存儲(chǔ)的地址recvx和sendx分別表示隊(duì)列中的索引

寫入的流程圖

讀的流程圖

常用語(yǔ)法

單向管道

func test1(c chan<- int) {} // 只讀
func test2(c <-chan int) {} // 只寫

可以傳遞chan的讀或者寫這樣在方法中只能進(jìn)行一種操作

select多路監(jiān)聽

使用select可以監(jiān)聽多個(gè)channel的讀或者寫,select如果不寫default的話,會(huì)阻塞groutine有可以讀取到的才會(huì)喚醒,寫了default的話如果都不滿足條件就會(huì)執(zhí)行default中的代碼不會(huì)阻塞

func main() {
    c1 := make(chan string)
    c2 := make(chan string)
    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(1 * time.Second)
        c2 <- "two"
    }()
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println(msg1)
        case msg2 := <-c2:
            fmt.Println(msg2)
        }
    }
    fmt.Println("執(zhí)行完畢")
}

上述代碼的結(jié)果是運(yùn)行之后1秒輸出one和two然后輸出執(zhí)行完畢

for-range

可以使用for-range的方式去channel中不斷的讀取數(shù)據(jù)它會(huì)在沒(méi)有數(shù)據(jù)的時(shí)候阻塞線程

func main() {
   c1 := make(chan string)
   go func() {
      for {
         time.Sleep(1 * time.Second)
         c1 <- "one"
      }
   }()
   chanRange(c1)
   fmt.Println("執(zhí)行完畢")
}
func chanRange(c chan string) {
   for e := range c {
      fmt.Println(e)
   }
}

上述代碼中使用了一個(gè)for-range在主線程去讀取數(shù)據(jù)會(huì)阻塞同時(shí)開啟一個(gè)groutine去每秒鐘寫入一個(gè)one上述代碼的結(jié)果就是每秒輸出one并且"執(zhí)行完畢"永遠(yuǎn)也不會(huì)執(zhí)行

slice

切片是我們平時(shí)最常用的,它又稱為動(dòng)態(tài)數(shù)組,它的底層是類似于java中的arrayList的會(huì)根據(jù)當(dāng)前容量自動(dòng)擴(kuò)容,這樣如果不理解一下它的原理有的問(wèn)題是不好發(fā)現(xiàn)的

func main() {
   s1 := []int{1, 2}
   s2 := s1
   s2 = append(s2, 3)
   sliceRise(s1)
   sliceRise(s2)
   fmt.Println(s1, s2)
}
func sliceRise(s []int) {
   s = append(s, 0)
   for i := range s {
      s[i]++
   }
}

先看看這段代碼輸出結(jié)果是

這是為什么呢? 因?yàn)閟lice底層是使用一塊內(nèi)存地址的,只有當(dāng)容量不夠的時(shí)候才會(huì)創(chuàng)建新的地址,然后將之前的值復(fù)制上去

因?yàn)閟1是array它的空間就是2,s2=s1這樣s2和s1指向一塊地址,s2 = append(s2, 3)這個(gè)語(yǔ)句由于s2中的空間不夠因此需要擴(kuò)容2倍就導(dǎo)致s1和s2不是一塊地址了而是兩塊不同的,進(jìn)行增加操作的時(shí)候s1內(nèi)存不足新創(chuàng)建一塊導(dǎo)致增加的不是原本的數(shù),s2空間是4因此可以再裝一個(gè)數(shù)因此操作的時(shí)候還是操作原來(lái)的數(shù),這就導(dǎo)致s1中的數(shù)沒(méi)增加,s2中的數(shù)增加了

slice的源碼在runtime/slice.go中

type slice struct {
   array unsafe.Pointer
   len   int
   cap   int
}

它的結(jié)構(gòu)體也是非常簡(jiǎn)單,就是一個(gè)數(shù)組和長(zhǎng)度容積大小

切片在使用的時(shí)候就是a[low:hight]這種格式表示前閉后開

a = a[:len(a) - 1] // 表示刪除最后一個(gè)
a = a[1:] //表示刪除第一個(gè)
a = [1,2,3,4]
fmt.Println(a[1:3]) // 輸出結(jié)果為2,3

由于底層使用的是同一塊地址因此會(huì)出現(xiàn)下面的問(wèn)題

func main() {
    a := []int{1, 2, 3, 4, 5}
    b := a[1:3]
    b = append(b, 0)
    fmt.Println(a)
}

我們看到這里并沒(méi)有改變數(shù)組a只是操作切片b就導(dǎo)致a中的數(shù)據(jù)發(fā)生改變,因?yàn)檫@里的b,len大小為2但是cap的大小為4就導(dǎo)致在原來(lái)的地址上面修改了

因此提供了一種設(shè)置大小的方式就是第三個(gè)參數(shù)

b := a[1:3:3]

b的聲明改成這樣就可以讓cap的大小為2保證數(shù)據(jù)安全

數(shù)組的直接比較

同時(shí)這里也聊一聊go中數(shù)組的語(yǔ)法糖:我們可以直接使用==去比較兩個(gè)數(shù)組

a := [2]int{1,2}
b := [2]int{1,2}
fmt.Printf(a == b) // true

如果數(shù)組中長(zhǎng)度和里面的數(shù)都是相等的話可以使用==去比較兩個(gè)數(shù)組是否相等

map

map是我們最常用的數(shù)據(jù)結(jié)構(gòu)之一,如果學(xué)習(xí)過(guò)別的語(yǔ)言例如java就對(duì)map的數(shù)據(jù)結(jié)構(gòu)比較熟悉,比如擾動(dòng)函數(shù)、hashcode、負(fù)載因子、哈希沖突等名詞都十分熟悉

在go中map的實(shí)現(xiàn)是通過(guò)bucket這種方式實(shí)現(xiàn)的,其實(shí)就是一個(gè)數(shù)組,計(jì)算需要存入的值然后找到數(shù)組下標(biāo),找到bucket中每一個(gè)下標(biāo)代碼的是一個(gè)8長(zhǎng)度的數(shù)組同一個(gè)hashcode可以存8個(gè)值,如果出現(xiàn)哈希沖突就在這8數(shù)組上進(jìn)行拉鏈法追加

可以在runtime/hashmap中去查找grow的代碼

// grow the map
func (hmap *hmap) grow() {
    // ...
    // compute new size
    newBucketsCount := oldBucketsCount
    if !hmap.growing() {
        newBucketsCount = oldBucketsCount << 1
    }
    // ...
    newBuckets := makeBucketArray(newBucketsCount)
    // ...
    for i := 0; i < oldBucketsCount; i++ {
        // ...
        for e := oldBuckets[i].first; e != nil; e = e.next {
            // ...
            // rehash the key to find the new bucket
            bucket := hashKey(newBuckets, e.key)
            // ...
            // insert the element into the new bucket
            newBuckets[bucket].insert(e)
        }
    }
    // ...
}

擴(kuò)容過(guò)程就是創(chuàng)建一個(gè)長(zhǎng)度二倍的bucket然后對(duì)舊的每一個(gè)數(shù)進(jìn)行重新hash然后放入新的bucket中

在go中map是線程不安全的如果想要線程安全可以使用sync包中的map去實(shí)現(xiàn)

到此這篇關(guān)于Go語(yǔ)言常見(jiàn)數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Go數(shù)據(jù)結(jié)構(gòu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語(yǔ)言讀取YAML 配置文件的兩種方式分享

    Go語(yǔ)言讀取YAML 配置文件的兩種方式分享

    在日常開發(fā)中,YAML 格式的文件基本上被默認(rèn)為是配置文件,其內(nèi)容因?yàn)榭s進(jìn)帶來(lái)的層級(jí)感看起來(lái)非常直觀和整潔。本文分享了讀取YAML 配置文件的兩種方式,需要的可以參考一下
    2022-12-12
  • golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-08-08
  • vscode上搭建go開發(fā)環(huán)境詳細(xì)完整過(guò)程

    vscode上搭建go開發(fā)環(huán)境詳細(xì)完整過(guò)程

    這篇文章主要給大家介紹了關(guān)于vscode上搭建go開發(fā)環(huán)境的詳細(xì)完整過(guò)程,Go語(yǔ)言或?qū)⒊蔀樾碌闹髁﹂_發(fā)語(yǔ)言,Go是google開發(fā)的一種靜態(tài)強(qiáng)類型、編譯型、并發(fā)型,并具有垃圾回收功能的編程語(yǔ)言,所以我們有必要學(xué)習(xí)并掌握它,需要的朋友可以參考下
    2023-10-10
  • GoLang?channel關(guān)閉狀態(tài)相關(guān)操作詳解

    GoLang?channel關(guān)閉狀態(tài)相關(guān)操作詳解

    Channel?和?goroutine?的結(jié)合是?Go?并發(fā)編程的大殺器。而?Channel?的實(shí)際應(yīng)用也經(jīng)常讓人眼前一亮,通過(guò)與?select,cancel,timer?等結(jié)合,它能實(shí)現(xiàn)各種各樣的功能。接下來(lái),我們就要介紹GoLang?channel關(guān)閉狀態(tài)相關(guān)操作
    2022-10-10
  • 詳解Go語(yǔ)言RESTful JSON API創(chuàng)建

    詳解Go語(yǔ)言RESTful JSON API創(chuàng)建

    這篇文章主要介紹了詳解Go語(yǔ)言RESTful JSON API創(chuàng)建,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • go-kit組件使用hystrix中間件的操作

    go-kit組件使用hystrix中間件的操作

    這篇文章主要介紹了go-kit組件使用hystrix中間件的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • golang sync.Pool 指針數(shù)據(jù)覆蓋問(wèn)題解決

    golang sync.Pool 指針數(shù)據(jù)覆蓋問(wèn)題解決

    本文主要介紹了使用sync.Pool時(shí)遇到指針數(shù)據(jù)覆蓋的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • 使用go gin來(lái)操作cookie的講解

    使用go gin來(lái)操作cookie的講解

    今天小編就為大家分享一篇關(guān)于使用go gin來(lái)操作cookie的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-04-04
  • Golang精編49面試題匯總(選擇題)

    Golang精編49面試題匯總(選擇題)

    這篇文章主要介紹了Golang精編49面試題匯總(選擇題),本文章內(nèi)容詳細(xì),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,需要的朋友可以參考下
    2023-01-01
  • Golang實(shí)現(xiàn)組合模式和裝飾模式實(shí)例詳解

    Golang實(shí)現(xiàn)組合模式和裝飾模式實(shí)例詳解

    這篇文章主要介紹了Golang實(shí)現(xiàn)組合模式和裝飾模式,本文介紹組合模式和裝飾模式,golang實(shí)現(xiàn)兩種模式有共同之處,但在具體應(yīng)用場(chǎng)景有差異。通過(guò)對(duì)比兩個(gè)模式,可以加深理解,需要的朋友可以參考下
    2022-11-11

最新評(píng)論