go語言切片怎么生成

本篇內(nèi)容介紹了“go語言切片怎么生成”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

洪雅ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來市場(chǎng)廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!

在go語言中,切片(slice)是對(duì)數(shù)組的一個(gè)連續(xù)片段的引用,所以切片是一個(gè)引用類型,這個(gè)片段可以是整個(gè)數(shù)組,也可以是由起始和終止索引標(biāo)識(shí)的一些項(xiàng)的子集;切片的內(nèi)存分布是連續(xù)的,所以可以把切片當(dāng)做一個(gè)大小不固定的數(shù)組。切片有三個(gè)字段的數(shù)據(jù)結(jié)構(gòu):指向底層數(shù)組的指針、切片訪問的元素的個(gè)數(shù)(即長(zhǎng)度)和切片允許增長(zhǎng)到的元素個(gè)數(shù)(即容量)。

切片(slice)是對(duì)數(shù)組的一個(gè)連續(xù)片段的引用,所以切片是一個(gè)引用類型(因此更類似于 C/C++ 中的數(shù)組類型,或者 Python 中的 list 類型),這個(gè)片段可以是整個(gè)數(shù)組,也可以是由起始和終止索引標(biāo)識(shí)的一些項(xiàng)的子集,需要注意的是,終止索引標(biāo)識(shí)的項(xiàng)不包括在切片內(nèi)。

Go語言中切片的內(nèi)部結(jié)構(gòu)包含地址、大小和容量,切片一般用于快速地操作一塊數(shù)據(jù)集合,如果將數(shù)據(jù)集合比作切糕的話,切片就是你要的“那一塊”,切的過程包含從哪里開始(切片的起始位置)及切多大(切片的大小),容量可以理解為裝切片的口袋大小,如下圖所示。

go語言切片怎么生成
圖:切片結(jié)構(gòu)和內(nèi)存分配

切片的內(nèi)存分布是連續(xù)的,所以你可以把切片當(dāng)做一個(gè)大小不固定的數(shù)組。

切片有三個(gè)字段的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)包含 Go 語言需要操作底層數(shù)組的元數(shù)據(jù),這 3 個(gè)字段分別是指向底層數(shù)組的指針、切片訪問的元素的個(gè)數(shù)(即長(zhǎng)度)和切片允許增長(zhǎng)到的元素個(gè)數(shù)(即容量)。后面會(huì)進(jìn)一步講解長(zhǎng)度和容量的區(qū)別。

go語言切片怎么生成

從數(shù)組或切片生成新的切片

切片默認(rèn)指向一段連續(xù)內(nèi)存區(qū)域,可以是數(shù)組,也可以是切片本身。

從連續(xù)內(nèi)存區(qū)域生成切片是常見的操作,格式如下:

slice [開始位置 : 結(jié)束位置]

語法說明如下:

  • slice:表示目標(biāo)切片對(duì)象;

  • 開始位置:對(duì)應(yīng)目標(biāo)切片對(duì)象的索引;

  • 結(jié)束位置:對(duì)應(yīng)目標(biāo)切片的結(jié)束索引。

從數(shù)組生成切片,代碼如下:

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

其中 a 是一個(gè)擁有 3 個(gè)整型元素的數(shù)組,被初始化為數(shù)值 1 到 3,使用 a[1:2] 可以生成一個(gè)新的切片,代碼運(yùn)行結(jié)果如下:

[1 2 3]  [2]

其中 [2] 就是 a[1:2] 切片操作的結(jié)果。

從數(shù)組或切片生成新的切片擁有如下特性:

  • 取出的元素?cái)?shù)量為:結(jié)束位置 - 開始位置;

  • 取出元素不包含結(jié)束位置對(duì)應(yīng)的索引,切片最后一個(gè)元素使用 slice[len(slice)] 獲取;

  • 當(dāng)缺省開始位置時(shí),表示從連續(xù)區(qū)域開頭到結(jié)束位置;

  • 當(dāng)缺省結(jié)束位置時(shí),表示從開始位置到整個(gè)連續(xù)區(qū)域末尾;

  • 兩者同時(shí)缺省時(shí),與切片本身等效;

  • 兩者同時(shí)為 0 時(shí),等效于空切片,一般用于切片復(fù)位。

根據(jù)索引位置取切片 slice 元素值時(shí),取值范圍是(0~len(slice)-1),超界會(huì)報(bào)運(yùn)行時(shí)錯(cuò)誤,生成切片時(shí),結(jié)束位置可以填寫 len(slice) 但不會(huì)報(bào)錯(cuò)。

下面通過實(shí)例來熟悉切片的特性。

1) 從指定范圍中生成切片

切片和數(shù)組密不可分,如果將數(shù)組理解為一棟辦公樓,那么切片就是把不同的連續(xù)樓層出租給使用者,出租的過程需要選擇開始樓層和結(jié)束樓層,這個(gè)過程就會(huì)生成切片,示例代碼如下:

var highRiseBuilding [30]int
for i := 0; i < 30; i++ {
        highRiseBuilding[i] = i + 1
}
// 區(qū)間
fmt.Println(highRiseBuilding[10:15])
// 中間到尾部的所有元素
fmt.Println(highRiseBuilding[20:])
// 開頭到中間指定位置的所有元素
fmt.Println(highRiseBuilding[:2])

代碼輸出如下:

go語言切片怎么生成

代碼中構(gòu)建了一個(gè) 30 層的高層建筑,數(shù)組的元素值從 1 到 30,分別代表不同的獨(dú)立樓層,輸出的結(jié)果是不同的租售方案。

代碼說明如下:

  • 第 8 行,嘗試出租一個(gè)區(qū)間樓層。

  • 第 11 行,出租 20 層以上。

  • 第 14 行,出租 2 層以下,一般是商用鋪面。

切片有點(diǎn)像C語言里的指針,指針可以做運(yùn)算,但代價(jià)是內(nèi)存操作越界,切片在指針的基礎(chǔ)上增加了大小,約束了切片對(duì)應(yīng)的內(nèi)存區(qū)域,切片使用中無法對(duì)切片內(nèi)部的地址和大小進(jìn)行手動(dòng)調(diào)整,因此切片比指針更安全、強(qiáng)大。

2) 表示原有的切片

生成切片的格式中,當(dāng)開始和結(jié)束位置都被忽略時(shí),生成的切片將表示和原切片一致的切片,并且生成的切片與原切片在數(shù)據(jù)內(nèi)容上也是一致的,代碼如下:

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

a 是一個(gè)擁有 3 個(gè)元素的切片,將 a 切片使用 a[:] 進(jìn)行操作后,得到的切片與 a 切片一致,代碼輸出如下:

[1 2 3]

3) 重置切片,清空擁有的元素

把切片的開始和結(jié)束位置都設(shè)為 0 時(shí),生成的切片將變空,代碼如下:

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

代碼輸出如下:

go語言切片怎么生成

直接聲明新的切片

除了可以從原有的數(shù)組或者切片中生成切片外,也可以聲明一個(gè)新的切片,每一種類型都可以擁有其切片類型,表示多個(gè)相同類型元素的連續(xù)集合,因此切片類型也可以被聲明,切片類型聲明格式如下:

var name []Type

其中 name 表示切片的變量名,Type 表示切片對(duì)應(yīng)的元素類型。

下面代碼展示了切片聲明的使用過程:

// 聲明字符串切片
var strList []string

// 聲明整型切片
var numList []int

// 聲明一個(gè)空切片
var numListEmpty = []int{}

// 輸出3個(gè)切片
fmt.Println(strList, numList, numListEmpty)

// 輸出3個(gè)切片大小
fmt.Println(len(strList), len(numList), len(numListEmpty))

// 切片判定空的結(jié)果
fmt.Println(strList == nil)
fmt.Println(numList == nil)
fmt.Println(numListEmpty == nil)

代碼輸出結(jié)果:

go語言切片怎么生成

代碼說明如下:

  • 第 2 行,聲明一個(gè)字符串切片,切片中擁有多個(gè)字符串。

  • 第 5 行,聲明一個(gè)整型切片,切片中擁有多個(gè)整型數(shù)值。

  • 第 8 行,將 numListEmpty 聲明為一個(gè)整型切片,本來會(huì)在{}中填充切片的初始化元素,這里沒有填充,所以切片是空的,但是此時(shí)的 numListEmpty 已經(jīng)被分配了內(nèi)存,只是還沒有元素。

  • 第 11 行,切片均沒有任何元素,3 個(gè)切片輸出元素內(nèi)容均為空。

  • 第 14 行,沒有對(duì)切片進(jìn)行任何操作,strList 和 numList 沒有指向任何數(shù)組或者其他切片。

  • 第 17 行和第 18 行,聲明但未使用的切片的默認(rèn)值是 nil,strList 和 numList 也是 nil,所以和 nil 比較的結(jié)果是 true。

  • 第 19 行,numListEmpty 已經(jīng)被分配到了內(nèi)存,但沒有元素,因此和 nil 比較時(shí)是 false。

切片是動(dòng)態(tài)結(jié)構(gòu),只能與 nil 判定相等,不能互相判定相等。聲明新的切片后,可以使用 append() 函數(shù)向切片中添加元素。

使用 make() 函數(shù)構(gòu)造切片

如果需要?jiǎng)討B(tài)地創(chuàng)建一個(gè)切片,可以使用 make() 內(nèi)建函數(shù),格式如下:

make( []Type, size, cap )

其中 Type 是指切片的元素類型,size 指的是為這個(gè)類型分配多少個(gè)元素,cap 為預(yù)分配的元素?cái)?shù)量,這個(gè)值設(shè)定后不影響 size,只是能提前分配空間,降低多次分配空間造成的性能問題。

示例如下:

a := make([]int, 2)
b := make([]int, 2, 10)

fmt.Println(a, b)
fmt.Println(len(a), len(b))

代碼輸出如下:

go語言切片怎么生成

其中 a 和 b 均是預(yù)分配 2 個(gè)元素的切片,只是 b 的內(nèi)部存儲(chǔ)空間已經(jīng)分配了 10 個(gè),但實(shí)際使用了 2 個(gè)元素。

容量不會(huì)影響當(dāng)前的元素個(gè)數(shù),因此 a 和 b 取 len 都是 2。

溫馨提示

使用 make() 函數(shù)生成的切片一定發(fā)生了內(nèi)存分配操作,但給定開始與結(jié)束位置(包括切片復(fù)位)的切片只是將新的切片結(jié)構(gòu)指向已經(jīng)分配好的內(nèi)存區(qū)域,設(shè)定開始與結(jié)束位置,不會(huì)發(fā)生內(nèi)存分配操作。

切片的使用

切片的使用和數(shù)組是一模一樣的:

func main() {
    slice1 := []int{1,2,3,4}
    fmt.Println(slice1[1])
}

切片創(chuàng)建切片

切片之所以稱為切片,是因?yàn)樗皇菍?duì)應(yīng)底層數(shù)組的一部分,看如下所示代碼:

func main() {
    slice := []int{10, 20, 30, 40, 50}
    newSlice := slice[1:3]
}

為了說明上面的代碼,我們看下面的這張圖:
go語言切片怎么生成

第一個(gè)切片slice 能夠看到底層數(shù)組全部5 個(gè)元素的容量,不過之后的newSlice 就看不到。對(duì)于newSlice,底層數(shù)組的容量只有4 個(gè)元素。newSlice 無法訪問到它所指向的底層數(shù)組的第一個(gè)元素之前的部分。所以,對(duì)newSlice 來說,之前的那些元素就是不存在的。

需要記住的是,現(xiàn)在兩個(gè)切片共享同一個(gè)底層數(shù)組。如果一個(gè)切片修改了該底層數(shù)組的共享部分,另一個(gè)切片也能感知到,運(yùn)行下面的代碼:

func main() {
    slice := []int{10, 20, 30, 40, 50}
    newSlice := slice[1:3]

    slice[1] = 200
    fmt.Println(newSlice[0])
}

運(yùn)行結(jié)果如下:

200

切片只能訪問到其長(zhǎng)度內(nèi)的元素。試圖訪問超出其長(zhǎng)度的元素將會(huì)導(dǎo)致語言運(yùn)行時(shí)異常,比如對(duì)上面的newSlice,他只能訪問索引為1和2的元素(不包括3),比如:

func main() {
    slice := []int{10, 20, 30, 40, 50}
    newSlice := slice[1:3]

    fmt.Println(newSlice[3])
}

運(yùn)行代碼,控制臺(tái)會(huì)報(bào)錯(cuò):

panic: runtime error: index out of range

goroutine 1 [running]:
main.main()
    E:/go-source/go-arr/main.go:20 +0x11

子切片的容量

我們知道切片可以再生出切片,那么子切片的容量為多大呢?我們來測(cè)試一下:

func main() {
    slice := make([]int, 2, 10)
    slice1 := slice[1:2]
    fmt.Println(cap(slice1))
}

控制臺(tái)打印結(jié)果為:

9
9

從結(jié)果我們可以推測(cè),子切片的容量為底層數(shù)組的長(zhǎng)度減去切片在底層數(shù)組的開始偏移量,比如在上面的例子中,slice1的偏移值為1,底層數(shù)組的大小為10,所以兩者相減,得到結(jié)果9。

向切片中追加元素

go提供了append方法用于向切片中追加元素,如下所示:

func main() {
    slice := make([]int, 2, 10)
    slice1 := slice[1:2]
    slice2 := append(slice1, 1)
    slice2[0] = 10001
    fmt.Println(slice)
    fmt.Println(cap(slice2))
}

輸出結(jié)果如下:

[0 10001]
9

此時(shí)slice,slice1,slice2共享底層數(shù)組,所以只要一個(gè)切片改變了某一個(gè)索引的值,會(huì)影響到所有的切片,還有一點(diǎn)值得注意,就是slice2的容量為9,記住這個(gè)值。

為了說明問題,我把例子改為如下所示代碼:

func main() {
    slice := make([]int, 2, 10)
    slice1 := slice[1:2]
    slice2 := append(slice1, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2 = append(slice2, 1)
    slice2[0] = 10001
    fmt.Println(slice)
    fmt.Println(slice1)
    fmt.Println(cap(slice2))
}

此時(shí)我們?cè)俅未蛴〗Y(jié)果,神奇的事情出現(xiàn)了:

[0 0]
[0]
18

雖然我們改變0位置的值,但是并沒有影響到原來的slice和slice1,這是為啥呢?我們知道原始的slice2對(duì)應(yīng)的底層數(shù)組的容量為9,經(jīng)過我們一系列的append操作,原始的底層數(shù)組已經(jīng)無法容納更多的元素了,此時(shí)Go會(huì)分配另外一塊內(nèi)存,把原始切片從位置1開始的內(nèi)存復(fù)制到新的內(nèi)存地址中,也就是說現(xiàn)在的slice2切片對(duì)應(yīng)的底層數(shù)組和slice切片對(duì)應(yīng)的底層數(shù)組完全不是在同一個(gè)內(nèi)存地址,所以當(dāng)你此時(shí)更改slice2中的元素時(shí),對(duì)slice已經(jīng)來說,一點(diǎn)兒關(guān)系都沒有。

另外根據(jù)上面的打印結(jié)果,你也應(yīng)該猜到了,當(dāng)切片容量不足的時(shí)候,Go會(huì)以原始切片容量的2倍建立新的切片,在我們的例子中2*9=18,就是這么粗暴。

如何創(chuàng)建子切片時(shí)指定容量

在前面的例子中,我們創(chuàng)建子切片的時(shí)候,沒有指定子切片的容量,所以子切片的容量和我們上面討論的計(jì)算子切片的容量方法相等,那么我們?nèi)绾问謩?dòng)指定子切片的容量呢?

在這里我們借用《Go實(shí)戰(zhàn)》中的一個(gè)例子:

func main() {
    source := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}
    slice := source[2:3:4]
    fmt.Println(cap(slice))
}

如果你仔細(xì)看的話,上面的子切片的生成方式和普通的切片有所不同,[]里面有三個(gè)部分組成,,第一個(gè)值表示新切片開始元素的索引位置,這個(gè)例子中是2。第二個(gè)值表示開始的索引位置(2)加上希望包括的元素的個(gè)數(shù)(1),2+1 的結(jié)果是3,所以第二個(gè)值就是3。為了設(shè)置容量,從索引位置2 開始,加上希望容量中包含的元素的個(gè)數(shù)(2),就得到了第三個(gè)值4。所以這個(gè)新的切片slice的長(zhǎng)度為1,容量為2。還有一點(diǎn)大家一定要記住,你指定的容量不能比原先的容量,這里就是source的容量大,加入我們這樣設(shè)置的話:

func main() {
    source := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}
    slice := source[2:3:10]
    fmt.Println(cap(slice))
}

運(yùn)行結(jié)果如下,報(bào)錯(cuò)了,哈哈:

panic: runtime error: slice bounds out of range [::10] with capacity 5

goroutine 1 [running]:
main.main()
    E:/learn-go/slice/main.go:7 +0x1d

迭代切片

關(guān)于如何迭代切片,我們可以使用range配置來使用,如下:

func main() {
    slice:=[]int{1,2,4,6}
    for _, value:=range slice{
        fmt.Println(value)
    }
}

關(guān)于迭代切片,大家有一點(diǎn)需要注意,就以上面的例子為例,value只是slice中元素的副本,為啥呢?我們來驗(yàn)證這一點(diǎn):

func main() {
    slice:=[]int{1,2,4,6}
    for index, value:=range slice{
        fmt.Printf("value[%d],indexAddr:[%X],valueAddr:[%X],sliceAddr:[%X]\n",value,&index,&value,&slice[index])
    }
}

控制臺(tái)打印結(jié)果如下:

value[1],indexAddr:[C00000A0B8],valueAddr:[C00000A0D0],sliceAddr:[C000010380]
value[2],indexAddr:[C00000A0B8],valueAddr:[C00000A0D0],sliceAddr:[C000010388]
value[4],indexAddr:[C00000A0B8],valueAddr:[C00000A0D0],sliceAddr:[C000010390]
value[6],indexAddr:[C00000A0B8],valueAddr:[C00000A0D0],sliceAddr:[C000010398]

從上面的結(jié)果可以看到index和value的地址始終是不變的,所以它們始終是同一個(gè)變量,只是變量引用地址的內(nèi)容發(fā)生了變化,從而驗(yàn)證迭代的時(shí)候,只能是切片元素的副本,最后看看sliceAddr代表的地址相隔8個(gè)字節(jié),因?yàn)樵?4位系統(tǒng)上,每一個(gè)int類型的大小為8個(gè)字節(jié)。

函數(shù)間傳遞切片

函數(shù)間傳遞切片,也是以值的方式傳遞的,但是你還記得這篇博文開頭給出的切片的布局么?
go語言切片怎么生成
切片由三個(gè)部分組成,包括指向底層數(shù)組的指針,當(dāng)前切片的長(zhǎng)度,當(dāng)前切片的容量,所以切片本身并不大,我們來測(cè)試一個(gè)切片的大小:

func main() {
    slice:=[]int{1,2,4,6}
    fmt.Println(unsafe.Sizeof(slice))
}

測(cè)試結(jié)果為:

24

也就是這個(gè)slice切片的大小為24字節(jié),所以當(dāng)切片作為參數(shù)傳遞的時(shí)候,幾乎沒有性能開銷,還有很重要的一點(diǎn),參數(shù)生成的副本的地址指針和原始切片的地址指針是一樣的,因此,如果你在函數(shù)里面修改了切片,那么會(huì)影響到原始的切片,我們來驗(yàn)證這點(diǎn):

func main() {
    slice:=[]int{1,2,4,6}
    handleSlice(slice)
    fmt.Println(slice)
}

打印結(jié)果:

[100 2 4 6]

“go語言切片怎么生成”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

網(wǎng)站標(biāo)題:go語言切片怎么生成
URL標(biāo)題:http://m.kartarina.com/article44/jecoee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄面包屑導(dǎo)航移動(dòng)網(wǎng)站建設(shè)營(yíng)銷型網(wǎng)站建設(shè)網(wǎng)站營(yíng)銷ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化
主站蜘蛛池模板: 一本色道久久综合无码人妻| 无码人妻少妇伦在线电影| 亚洲欧洲av综合色无码 | 亚洲av永久无码精品国产精品 | 成人无码A区在线观看视频| 亚洲色偷拍另类无码专区| 精品人妻系列无码天堂| 国产av无码专区亚洲av毛片搜| av无码人妻一区二区三区牛牛| 精品无码成人片一区二区98| 亚洲日韩乱码中文无码蜜桃臀网站 | 无码毛片一区二区三区中文字幕 | 成人无码精品1区2区3区免费看| 亚洲国产精品无码一线岛国| 色欲aⅴ亚洲情无码AV| 亚洲国产成人精品无码一区二区| 13小箩利洗澡无码视频网站免费 | 精品无码久久久久久久久久| 亚洲国产成人精品无码区在线秒播| 亚洲Av无码乱码在线播放| 中文字幕无码免费久久99| 欧洲Av无码放荡人妇网站| 国产三级无码内射在线看| 亚洲人成影院在线无码观看| 国产久热精品无码激情| 精品久久久久久无码专区不卡| 国产成人无码精品一区二区三区| 亚洲精品人成无码中文毛片| 国产精品无码久久久久久久久久| 亚洲私人无码综合久久网| 久久久久成人精品无码中文字幕| 亚洲av日韩av无码| 久久久久久AV无码免费网站下载| 国产50部艳色禁片无码| 国产亚洲?V无码?V男人的天堂 | 永久免费AV无码网站在线观看| 无码高潮爽到爆的喷水视频app| 精品久久无码中文字幕| 亚洲av日韩aⅴ无码色老头| 亚洲AV无码一区二区三区牛牛| 久久久无码精品亚洲日韩按摩 |