Go語言學習、切片定義和使用

一、切片的定義和使用

package main

import "fmt"

func main() {
	//數組定義 var 數組名 [元素個數] 數據類型
	//切片定義 var 切片名 [] 數據類型
	var  s [] int
	fmt.Println(s)
}

輸出結果

[]

可以看到,我們在上面的程序中,將切片的定義和數組的定義做了對比,切片的定義時,不需要任何元素個數

當定義完成切片後,我們做打印,發現切片爲空,因沒有任何數據

package main

import "fmt"

func main() {
	//數組定義 var 數組名 [元素個數] 數據類型
	//切片定義 var 切片名 [] 數據類型
	var  s [] int
	s[0] = 123 //這裏直接給切片複製,會報錯,因爲切片的長度爲零
	fmt.Println(s)
}

輸出結果:

panic: runtime error: index out of range

goroutine 1 [running]:
main.main()
	D:/go_work/切片.go:9 +0x17

我們定義完切片後,直接通過切片下標進行賦值操作,會出現上述錯誤,因爲這時定義的切片長度爲0,不能直接賦值

我們通過下面的方法,進行切片的定義

package main

import "fmt"

func main() {
	//make([] 數據類型, 切片長度)
	s := make([] int , 5) //通過自動推導類型,創建一個切片,類型爲int,長度爲5
	//通過下標爲切片賦值
	s[0] = 123
	s[1] = 456
	s[2] = 789
	fmt.Println(s)
 }

二、make方法,創建切片操作,輸出結果:

[123 456 789 0 0]

我們在沒有對切片的元素進行賦值時,該元素的結果爲0,也會被打印。

通過len查看切片長度

	//通過len查看切片的長度
	fmt.Println(len(s))

結果:

5

是我們定義的長度。

三、切片的擴展:

package main

import "fmt"

func main() {
	//make([] 數據類型, 切片長度)
	s := make([] int , 5) //通過自動推導類型,創建一個切片,類型爲int,長度爲5
	//通過下標爲切片賦值
	s[0] = 123
	s[1] = 456
	s[2] = 789
	s[3] = 111
	s[4] = 222
	//s[5] = 333 //我們不能直接給切片進行,越界賦值,會直接報越界錯誤
	//但是通過append,可以直接添加切片元素
	s = append(s, 333)
	fmt.Println(s)

 }

輸出結果:

[123 456 789 111 222 333]

我們可以看到,通過,s[5]  = 333 這種方式,不能直接給切片進行擴展操作,如果想要給一個長度已滿的切片進行賦值操作,則需要我們通過append方法,進行添加元素操作。

我們在看看,添加完後的切片長度:

fmt.Println(len(s))

結果爲:6

也可以通過append,添加多個元素,需要在元素值後面用逗號隔開:

s = append(s, 333,444,555)

四、切片的遍歷操作:

1、

package main

import "fmt"

func main() {
	//make([] 數據類型, 切片長度)
	s := make([] int , 5) //通過自動推導類型,創建一個切片,類型爲int,長度爲5
	//通過下標爲切片賦值
	s[0] = 123
	s[1] = 456
	s[2] = 789
	s[3] = 111
	s[4] = 222
	// 切片的遍歷
	for i:=0; i<len(s);i++{
		fmt.Println(s[i])
	}
 }

結果:

123
456
789
111
222

通過循環遍歷出切片的值

2、

	for i,v:=range s{
		fmt.Println(i,v)
	}

結果:

0 123
1 456
2 789
3 111
4 222

五、切片初始化操作:

我們在不適用make創建切片的時候,可以直接對切片進行初始化操作。

package main

import "fmt"

func main() {
	//這種創建切片的方式,跟創建數組類似
	//不寫元素個數的,叫切片,必須寫元素個數的叫數組
	//切片與數組都是連續的存儲空間,切片可以擴展,元素不能擴展。
	//切片在內存中存儲的區域是堆區。數組的數據存儲在內存中的棧區
	var s [] int = []int{1,2,3,4,5}
	fmt.Println(s)
	
}

結果:

[1 2 3 4 5]

六、切片的容量:

package main

import "fmt"

func main() {
	var s [] int = []int{1,2,3,4,5}
	fmt.Println(s)
	//切片的長度
	fmt.Println("長度 = ",len(s))
	// 切片的容量
	fmt.Println("容量 = ", cap(s))
}

結果:

[1 2 3 4 5]
長度 =  5
容量 =  5

可以看到,我們使用cap方法來獲得切片容量大小

我們在s中,添加一個元素

package main

import "fmt"

func main() {
	var s [] int = []int{1,2,3,4,5}
	fmt.Println(s)
	//添加元素:
	s = append(s, 6,7,8,9)

	//切片的長度
	fmt.Println("長度 = ",len(s))
	// 切片的容量
	fmt.Println("容量 = ", cap(s))

	//添加一個元素到s中
	s = append(s, 10,11,12,13)
	//切片的長度
	fmt.Println("長度 = ",len(s))
	// 切片的容量
	fmt.Println("容量 = ", cap(s)) 
}

輸出結果:

[1 2 3 4 5]
長度 =  9
容量 =  12
長度 =  13
容量 =  24

可以看到,容量一定是大於等於長度的,且容量的每次擴展是上一次容量的倍數。

當容量大小,超過當前容量,纔會自動擴展。

如果整體數據,沒有超過1024個字節,每次擴展爲上一次的倍數,如果超過1024,則每次擴展是上次的四分之一。

舉例:

package main

import "fmt"

func main() {
	 s := make([]int, 0 ,1) //容量爲1
	 oldCap := cap(s)
	 for i:=0; i<20; i++{
	 	s = append(s,i)
	 	newCap:= cap(s)
	 	if oldCap < newCap{
	 		fmt.Printf("cap: %d========> %d\n", oldCap, newCap)
	 		oldCap = newCap
		}
	 }
}

輸出結果:

cap: 1========> 2
cap: 2========> 4
cap: 4========> 8
cap: 8========> 16
cap: 16========> 32

通過上面這個例子,我們可以看到,是切片的擴容是通過2倍進行的

在看下,下面這個例子:

package main

import "fmt"

func main() {
	 s := make([]int, 0 ,1) //容量爲1
	 oldCap := cap(s)
	 for i:=0; i<200000; i++{ //我們吧循環條件修改變大
	 	s = append(s,i)
	 	newCap:= cap(s)
	 	if oldCap < newCap{
	 		fmt.Printf("cap: %d========> %d\n", oldCap, newCap)
	 		oldCap = newCap
		}
	 }
}

結果:

cap: 512========> 1024  //兩倍擴容
cap: 1024========> 1344 //不再是兩倍擴容
cap: 1344========> 1696
cap: 1696========> 2368
cap: 2368========> 3072
cap: 3072========> 4096
cap: 4096========> 5120
cap: 5120========> 6816
cap: 6816========> 10240
cap: 10240========> 14336
cap: 14336========> 18432
cap: 18432========> 24576
cap: 24576========> 30720
cap: 30720========> 38912
cap: 38912========> 49152
cap: 49152========> 61440
cap: 61440========> 77824
cap: 77824========> 98304
cap: 98304========> 122880
cap: 122880========> 153600
cap: 153600========> 192512
cap: 192512========> 241664

當容量小於1024時是按照2倍容量擴容,當大於等於1024是不是按照2倍容量擴容。

七、切片的截取:

首先說一下切片的截取操作,所謂截取就是從切片中獲取指定的數據。

    我們通過如下程序給大家解釋一下:

package main

import "fmt"

func main() {
	//定義切片,並完成初始化
	s:= []int {10,20,30,40,50}

	// 從切片s中截取數據
	slice:= s[0:3:5]
	fmt.Println(slice)
}

結果:

[10 20 30]

s[0:3:5]是什麼意思呢?

我們可以使用s[low:high:max]來表示

 第一個數(low)表示下標的起點(從該位置開始截取),如果low取值爲0表示從第一個元素開始截取,也就是對應的切片s中的10

第二個數(high)表示取到哪結束,也就是下標的終點(不包含該位置),3表示取出下標是0,1,2的數據(10,20,30),不包括下標爲3的數據,那麼也就是說取出的數據長度是3. 可以根據公式:3-0 計算(len=high-low),也就是第二個數減去第一個數,差就是數據長度。在這裏可以將長度理解成取出的數據的個數。

第三個數用來計算容量,所謂容量:是指切片目前可容納的最多元素個數。通過公式5-0計算(cap=max-low),也就是第三個數據減去第一個數。該案例中容量爲5

關於切片的截取還有其它的操作,如下圖所示:

操作

含義

s[n]

切片s中索引位置爲n的項

s[:]

從切片s的索引位置0到len(s)-1處所獲得的切片

s[low:]

從切片s的索引位置low到len(s)-1處所獲得的切片

s[:high]

從切片s的索引位置0到high處所獲得的切片,len=high

s[low:high]

從切片s的索引位置low到high處所獲得的切片,len=high-low

s[low:high:max]

從切片s的索引位置low到high處所獲得的切片,len=high-low,cap=max-low

len(s)

切片s的長度,總是<=cap(s)

cap(s)

切片s的容量,總是>=len(s)

通過下面的例子,我們簡單的演示一下:

1、

package main

import "fmt"

func main() {
	//定義切片,並完成初始化
	s:= []int {0,1,2,3,4,5,6,7,8,9}
	// 從切片s中截取數據
	slice:= s[:] //不指定容量和長度一樣
	fmt.Println("slice = ",slice)
	fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}

輸出結果:

slice =  [0 1 2 3 4 5 6 7 8 9]
len = 10, cap = 10

2、

package main

import "fmt"

func main() {
	//定義切片,並完成初始化
	s:= []int {0,1,2,3,4,5,6,7,8,9}
	// 從切片s中截取數據
	slice:= s[3:] //從下標3開始
	fmt.Println("slice = ",slice)
	fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}

結果:

slice =  [3 4 5 6 7 8 9]
len = 7, cap = 7

3、

package main

import "fmt"

func main() {
	//定義切片,並完成初始化
	s:= []int {0,1,2,3,4,5,6,7,8,9}
	// 從切片s中截取數據
	slice:= s[:6] //從下0開始,取6個元素,容量是10
	fmt.Println("slice = ",slice)
	fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}

結果:

slice =  [0 1 2 3 4 5]
len = 6, cap = 10

4、

package main

import "fmt"

func main() {
	//定義切片,並完成初始化
	s:= []int {0,1,2,3,4,5,6,7,8,9}
	// 從切片s中截取數據
	slice:= s[2:5] //從下2開始(包含該元素),取到下標5爲止(不包含該元素),切片長度爲3,容量是8,10-2
	fmt.Println("slice = ",slice)
	fmt.Printf("len = %d, cap = %d\n", len(slice), cap(slice))
}

結果:

slice =  [2 3 4]
len = 3, cap = 8

八、copy函數使用

針對切片操作常用的方法除了append( )方法以外,還有copy方法.

基本語法:copy(切片1,切片2)

將第二個切片裏面的元素,拷貝到第一個切片中。

下面通過一個案例,看一下該方法的使用:

package main

import "fmt"

func main() {
	s1 := []int{1,2}
	s2 := []int{4,5,6,7,8}
	copy(s2,s1) //將s1中的元素,拷貝到s2中
	fmt.Print(s2)
}

結果:

[1 2 6 7 8]

通過以上結果可以分析出,直接將1切片中兩個元素拷貝到s2元素中相同的位置。而s2原有的元素備替換掉。

在看下面的:

package main

import "fmt"

func main() {
	s1 := []int{1,2}
	s2 := []int{4,5,6,7,8}
	copy(s1,s2) //將s2中的元素,拷貝到s1中
	fmt.Print(s1)
}

輸出結果:

[4 5]

而,將s2拷貝到s1,則會吧s1中的元素替換掉。

今天就講到這裏。感謝各位閱讀,歡迎點贊評論。謝謝!!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章