最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang之?dāng)?shù)組切片的具體用法

 更新時(shí)間:2022年07月19日 09:42:41   作者:假裝懂編程  
本文主要介紹了golang之?dāng)?shù)組切片的具體用法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

數(shù)組

go開發(fā)者在日常的工作中slice算是用的比較多的了,在介紹slice之前,我們先了解下數(shù)組,數(shù)組相信大家都不陌生,數(shù)組的數(shù)據(jù)結(jié)構(gòu)比較簡(jiǎn)單,它在內(nèi)存中是連續(xù)的。以一個(gè)存了10個(gè)數(shù)字的數(shù)組為例來說:

a:=[10]int{0,1,2,3,4,5,6,7,8,9}

它在內(nèi)存中大概是這樣的:

得益于連續(xù)性,所以數(shù)組的特點(diǎn)就是:

  • 大小固定
  • 訪問快,復(fù)雜度為O(1);
  • 插入和刪除元素因?yàn)橐苿?dòng)元素,所以相比查詢會(huì)慢。 當(dāng)我們要訪問一個(gè)越界的元素的元素時(shí),go甚至編輯都不通過:
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(a[10])
// invalid array index 10 (out of bounds for 10-element array)

切片

相比數(shù)組,go的slice(切片)要相對(duì)靈活些,比較大的不同點(diǎn)就是slice的長(zhǎng)度可以不固定,創(chuàng)建的時(shí)候不用指明長(zhǎng)度,在go中slice是一種設(shè)計(jì)過的數(shù)據(jù)結(jié)構(gòu):

type slice struct {
   array unsafe.Pointer //指針
   len   int //長(zhǎng)度
   cap   int //容量
}

slice的底層其實(shí)還是數(shù)組,通過指針指向它底層的數(shù)組,len是slice的長(zhǎng)度,cap是slice的容量,slice添加元素時(shí),且cap容量不足時(shí),會(huì)根據(jù)策略擴(kuò)容。

切片的創(chuàng)建

直接聲明

var s []int

通過直接聲明的slice,它是個(gè)nil slice,它的長(zhǎng)度和容量都是0,且不指向任何底層數(shù)組,nil切片和空切片是不一樣的,接下來會(huì)介紹。

new方式初始化

s:=*new([]int) 

new的方式和直接聲明的方式區(qū)別不大,最終產(chǎn)出的都是一個(gè)nil的slice。

字面量

s1 := []int{0, 1, 2}
s2 := []int{0, 1, 2, 4: 4}
s3 := []int{0, 1, 2, 4: 4, 5, 6, 9: 9}
fmt.Println(s1, len(s1), cap(s1)) //[0 1 2] 3 3
fmt.Println(s2, len(s2), cap(s2)) //[0 1 2 0 4] 5 5
fmt.Println(s3, len(s3), cap(s3)) //[0 1 2 0 4 5 6 0 0 9] 10 10

字面量創(chuàng)建的slice,默認(rèn)長(zhǎng)度和容量是相等的,需要注意的是如果我們單獨(dú)指明了某個(gè)索引的值,那么在這個(gè)索引值前面的元素如果未聲明的話,就會(huì)是slice的類型的默認(rèn)值。

make方式

s := make([]int, 5, 6)
fmt.Println(s, len(s), cap(s)) //[0 0 0 0 0] 5 6

通過make可以指定slice的長(zhǎng)度和容量。

截取方式

切片可以從數(shù)組或者其他切片中截取獲得,這時(shí)新的切片會(huì)和老的數(shù)組或切片共享一個(gè)底層數(shù)組,不管誰修改了數(shù)據(jù),都會(huì)影響到底層的數(shù)組,但是如果新的切片發(fā)生了擴(kuò)容,那么底層的數(shù)組就不是同一個(gè)。

s[:]

a := []int{0, 1, 2, 3, 4}
b := a[:]
fmt.Println(b, len(b), cap(b)) //[0 1 2 3 4] 5 5

通過: 獲取 [0,len(a)-1]的切片,等同于整個(gè)切片的引用。

s[i:]

a := []int{0, 1, 2, 3, 4}
b := a[1:]
fmt.Println(b, len(b), cap(b)) //[1 2 3 4] 4 4

通過指定切片的開始位置來獲取切片,它是左閉的包含左邊的元素,此時(shí)它的容量cap(b)=cap(a)-i。這里要注意界限問題,a[5:]的話,相當(dāng)于走到數(shù)組的尾巴處,什么元素也沒了,此時(shí)就是個(gè)空切片,但是如果你用a[6:]的話,那么就會(huì)報(bào)錯(cuò),超出了數(shù)組的界限。

a := []int{0, 1, 2, 3, 4}
b := a[5:] //[]
c := a[6:] //runtime error: slice bounds out of range [6:5]

c雖然報(bào)錯(cuò)了,但是它只是運(yùn)行時(shí)報(bào)錯(cuò),編譯還是能通過的。

s[:j]

a := []int{0, 1, 2, 3, 4}
b := a[:4]
fmt.Println(b, len(b), cap(b)) //[0 1 2 3] 4 5

獲取[0-j)的數(shù)據(jù),注意右邊是開區(qū)間,不包含j,同時(shí)它的cap和j沒關(guān)系,始終是cap(b) = cap(a),同樣注意不要越界。

s[i:j]

a := []int{0, 1, 2, 3, 4}
b := a[2:4]
fmt.Println(b, len(b), cap(b)) //[2 3] 2 3

獲取[i-j)的數(shù)據(jù),注意右邊是開區(qū)間,不包含j,它的cap(b) = cap(a)-i。

s[i:j:x]

a := []int{0, 1, 2, 3, 4}
b := a[1:2:3]
fmt.Println(b, len(b), cap(b)) //[1] 1 2

通過上面的例子,我們可以發(fā)現(xiàn)切片b的cap其實(shí)和j沒什么關(guān)系,和i存在關(guān)聯(lián),不管j是什么,始終是cap(b)=cap(a)-i,x的出現(xiàn)可以修改b的容量,當(dāng)我們?cè)O(shè)置x后,cap(b) = x-i而不再是cap(a)-i了。

看個(gè)例子

s0 := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := s0[3:6] //[3 4 5] 3 7

s1是對(duì)s0的切片,所以它們大概是這樣:

s2 := s1[1:3:4]

這時(shí)指定個(gè)s2,s2是對(duì)s1的切片,并且s2的len=2,cap=3,所以大概長(zhǎng)這樣:

s1[1] = 40
fmt.Println(s0, s1, s2)// [0 1 2 3 40 5 6 7 8 9] [3 40 5] [40 5]

這時(shí)把s1[1]修改成40,因?yàn)闆]有涉及到擴(kuò)容,s0、s1、s2重疊部分都指向同一個(gè)底層數(shù)組,所以最終發(fā)現(xiàn)s0、s2對(duì)應(yīng)的位置都變成了40。

s2 = append(s2, 10)
fmt.Println(s2, len(s2), cap(s2)) //[40 5 10] 3 3

再向s2中添加一個(gè)元素,因?yàn)閟2還有一個(gè)空間,所以不用發(fā)生擴(kuò)容。

s2 = append(s2, 11)
fmt.Println(s2, len(s2), cap(s2)) //[40 5 10 11] 4 6

繼續(xù)向s2中添加一個(gè)元素,此時(shí)s2已經(jīng)沒有空間了,所以會(huì)觸發(fā)擴(kuò)容,擴(kuò)容后指向一個(gè)新的底層數(shù)據(jù),和原來的底層數(shù)組解耦了。

此時(shí)無論怎么修改s2都不會(huì)影響到s1和s2。

切片的擴(kuò)容

slice的擴(kuò)容主要通過growslice函數(shù)上來處理的:

func growslice(et *_type, old slice, cap int) slice {
    ....
    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
            newcap = cap
    } else {
        if old.len < 1024 {
              newcap = doublecap
        } else {
            // Check 0 < newcap to detect overflow
            // and prevent an infinite loop.
            for 0 < newcap && newcap < cap {
                  newcap += newcap / 4
            }
            // Set newcap to the requested cap when
            // the newcap calculation overflowed.
            if newcap <= 0 {
                 newcap = cap
            }
        }
    }
    ....
    return slice{p, old.len, newcap}
}

入?yún)⒄f明下:

  • et是slice的類型。
  • old是老的slice。
  • cap是擴(kuò)容后的最低容量,比如原來是4,append加了一個(gè),那么cap就是5。 所以上面的代碼解釋為:
  • 如果擴(kuò)容后的最低容量大于老的slice的容量的2倍,那么新的容量等于擴(kuò)容后的最低容量。
  • 如果老的slice的長(zhǎng)度小于1024,那么新的容量就是老的slice的容量的2倍
  • 如果老的slice的長(zhǎng)度大于等于1024,那么新的容量就等于的容量不停的1.25倍,直至大于擴(kuò)容后的最低容量。 這里需要說明下關(guān)于slice的擴(kuò)容網(wǎng)上很多文章都說小于1024翻倍擴(kuò)容,大于1024每次1.25倍擴(kuò)容,其實(shí)就是基于這段代碼,但其實(shí)這不全對(duì),我們來看個(gè)例子:
a := []int{1, 2}
fmt.Println(len(a), cap(a)) //2 2
a = append(a, 2, 3, 4)
fmt.Println(len(a), cap(a)) // 5 6

按照規(guī)則1,這時(shí)的cap應(yīng)該是5,結(jié)果是6。

a := make([]int, 1280, 1280)
fmt.Println(len(a), cap(a)) //1280 1280
a = append(a, 1)
fmt.Println(len(a), cap(a), 1280*1.25) //1281 1696 1600

按照規(guī)則3,這時(shí)的cap應(yīng)該是原來的1.25倍,即1600,結(jié)果是1696。

內(nèi)存對(duì)齊

其實(shí)上面兩個(gè)擴(kuò)容,只能說不是最終的結(jié)果,go還會(huì)做一些內(nèi)存對(duì)齊的優(yōu)化,通過內(nèi)存對(duì)齊可以提升讀取的效率。

// 內(nèi)存對(duì)齊
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
capmem = roundupsize(capmem)
newcap = int(capmem / et.size)

空切片和nil切片

空切片:slice的指針不為空,len和cap都是0
nil切片:slice的指針不指向任何地址即array=0,len和cap都是0

nil
var a []inta:=make([]int,0)
a:=*new([]int)a:=[]int{}

空切片雖然地址不為空,但是這個(gè)地址也不代表任何底層數(shù)組的地址,空切片在初始化的時(shí)候會(huì)指向一個(gè)叫做zerobase的地址,

var zerobase uintptr
if size == 0 {
      return unsafe.Pointer(&zerobase)
}

所有空切片的地址都是一樣的。

var a1 []int
a2:=*new([]int)
a3:=make([]int,0)
a4:=[]int{}

fmt.Println(*(*[3]int)(unsafe.Pointer(&a1))) //[0 0 0]
fmt.Println(*(*[3]int)(unsafe.Pointer(&a2))) //[0 0 0]
fmt.Println(*(*[3]int)(unsafe.Pointer(&a3))) //[824634101440 0 0]
fmt.Println(*(*[3]int)(unsafe.Pointer(&a4))) //[824634101440 0 0]

數(shù)組是值傳遞,切片是引用傳遞?

func main() {
   array := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   changeArray(array)
   fmt.Println(array) //[0 1 2 3 4 5 6 7 8 9]
   changeSlice(slice)
   fmt.Println(slice) //[1 1 2 3 4 5 6 7 8 9]
}

func changeArray(a [10]int) {
   a[0] = 1
}

func changeSlice(a []int) {
   a[0] = 1
}
  • 定義一個(gè)數(shù)組和一個(gè)切片
  • 通過changeArray改變數(shù)組下標(biāo)為0的值
  • 通過changeSlice改變切片下標(biāo)為0的值
  • 原數(shù)組值未被修改,原切片的值已經(jīng)被修改 這個(gè)表象看起來像是slice是指針傳遞似的,但是如果我們這樣呢:
func main() {
   slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   changeSlice(slice)//[0 1 2 3 4 5 6 7 8 9]
}
func changeSlice(a []int) {
   a = append(a, 99)
}

會(huì)發(fā)現(xiàn)原slice的值并沒有被改變,這是因?yàn)槲覀冇昧薬ppend,append之后,原slice的容量已經(jīng)不夠了,這時(shí)候會(huì)copy出一個(gè)新的數(shù)組。其實(shí)go的函數(shù)參數(shù)傳遞,只有值傳遞,沒有引用傳遞,當(dāng)slice的底層數(shù)據(jù)沒有改變的時(shí)候,怎么修改都會(huì)影響原底層數(shù)組,當(dāng)slice發(fā)生擴(kuò)容時(shí),擴(kuò)容后就是新的數(shù)組,那么怎么修改這個(gè)新的數(shù)組都不會(huì)影響原來的數(shù)組。

數(shù)組和slice能不能比較

只有長(zhǎng)度相同,類型也相同的數(shù)組才能比較

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

a:=[2]int{1,2}
b:=[3]int{1,2,3}
fmt.Println(a==b) //invalid operation: a == b (mismatched types [2]int and [3]int)

a:=[2]int{1,2}
b:=[2]int8{1,2}
fmt.Println(a==b) //invalid operation: a == b (mismatched types [2]int and [2]int8)

slice只能和nil做比較,其余的都不能比較

a:=[]int{1,2}
b:=[]int{1,2}
fmt.Println(a==b)//invalid operation: a == b (slice can only be compared to nil)

但是需要注意的是,兩個(gè)都是nil的slice也不能進(jìn)行比較,它只能和nil對(duì)比,這里的nil是真真實(shí)實(shí)的nil。

var a []int
var b []int
fmt.Println(a == b) //invalid operation: a == b (slice can only be compared to nil)
fmt.Println(a == nil) //true

到此這篇關(guān)于golang之?dāng)?shù)組切片的具體用法的文章就介紹到這了,更多相關(guān)golang 數(shù)組切片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go Generate 代替 Makefile使用方法詳解

    Go Generate 代替 Makefile使用方法詳解

    這篇文章主要為大家介紹了Go Generate 代替 Makefile使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 解析golang 標(biāo)準(zhǔn)庫template的代碼生成方法

    解析golang 標(biāo)準(zhǔn)庫template的代碼生成方法

    這個(gè)項(xiàng)目的自動(dòng)生成代碼都是基于 golang 的標(biāo)準(zhǔn)庫 template 的,所以這篇文章也算是對(duì)使用 template 庫的一次總結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-11-11
  • Go語言每天必學(xué)之switch語句

    Go語言每天必學(xué)之switch語句

    這篇文章主要為大家詳細(xì)介紹了Go語言每天必學(xué)之switch語句的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • golang defer執(zhí)行順序全面詳解

    golang defer執(zhí)行順序全面詳解

    這篇文章主要為大家介紹了golang defer執(zhí)行順序全面詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • golang 刪除切片的某個(gè)元素及剔除切片內(nèi)的零值方式

    golang 刪除切片的某個(gè)元素及剔除切片內(nèi)的零值方式

    這篇文章主要介紹了golang 刪除切片的某個(gè)元素及剔除切片內(nèi)的零值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang在圖像中繪制矩形框的示例代碼

    Golang在圖像中繪制矩形框的示例代碼

    這篇文章主要介紹了Golang在圖像中繪制矩形框的示例代碼,文中有詳細(xì)的代碼示例供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下
    2008-08-08
  • golang如何修改json文件內(nèi)容的方法示例

    golang如何修改json文件內(nèi)容的方法示例

    這篇文章主要介紹了golang如何修改json文件內(nèi)容的方法示例,使用一個(gè)例子說明golang如何訪問和修改json文件,有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • golang構(gòu)建工具M(jìn)akefile使用詳解

    golang構(gòu)建工具M(jìn)akefile使用詳解

    這篇文章主要為大家介紹了golang構(gòu)建工具M(jìn)akefile的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Golang爬蟲框架colly使用淺析

    Golang爬蟲框架colly使用淺析

    這篇文章主要介紹了Golang爬蟲框架colly的使用,colly是Go實(shí)現(xiàn)的比較有名的一款爬蟲框架,而且Go在高并發(fā)和分布式場(chǎng)景的優(yōu)勢(shì)也正是爬蟲技術(shù)所需要的,感興趣想要詳細(xì)了解可以參考下文
    2023-05-05
  • Windows下在CMD下執(zhí)行Go出現(xiàn)中文亂碼的解決方法

    Windows下在CMD下執(zhí)行Go出現(xiàn)中文亂碼的解決方法

    在cmd下運(yùn)行g(shù)o程序或者是GOLAND的Terminal下運(yùn)行g(shù)o程序會(huì)出現(xiàn)中文亂碼的情況。本文就詳細(xì)的介紹下解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-12-12

最新評(píng)論

宁夏| 德州市| 江源县| 临洮县| 成武县| 阳西县| 鄯善县| 台南市| 扎囊县| 罗甸县| 蕲春县| 武胜县| 衡阳市| 郸城县| 岳阳县| 登封市| 怀宁县| 牙克石市| 绥宁县| 栾川县| 涞水县| 墨玉县| 四会市| 金溪县| 兴城市| 加查县| 安溪县| 高雄县| 吉木萨尔县| 筠连县| 连城县| 军事| 南投县| 邻水| 龙岩市| 新营市| 吉林市| 定州市| 涡阳县| 盐津县| 沙河市|