Go 数组与切片
#### Go 數組與切片
***渡風渡塵緣,修行亦修心***
##### 數組
數組是可以存放多個同一類型的數據的集合,數組也是一種數據類型,它是值類型;
數組如何定義?
var 數組名 [數組大小]數據類型: var a [5]int package mainimport "fmt"func main(){var intArr [3]intintArr[0] = 10intArr[1] = 20intArr[2] = 30fmt.Println(intArr) }
數組在內存中的布局:
1. 數組的地址可以通過數組名獲取: &intArr;
2. 數組的第一個元素地址就是數組的地址:;
3. 數組的各個元素的地址間隔根據數組的類型決定: int64-> 8 字節,int32-> 4字節;
4. 數組如果未給元素賦值,元素的默認值為類型的零值: int64->0,string->"",float64->0;
5. 訪問數組中的元素: 使用下標索引方式(左閉右開方式),這個與其它語言一樣,超出下標索引將會出錯,索引越界;
6. 數組的遍歷有兩種方式: for 傳統方式; for range 方式 ; package mainimport "fmt"func main(){var a [5]inta[0] = 1a[1] = 2a[2] = 3// 默認值: a[3] = 0 , a[4] = 0// 下標越界: a[5] = 10 // 錯誤, 下標越界fmt.Println(a) //[1 2 3 0 0 ]fmt.Printf("a address is %p\n",&a) // 0xc000078030fmt.Printf("a[0] address is %p\n",&a[0]) //0xc000078030fmt.Printf("a[1] address is %p\n",&a[1]) //0xc000078038fmt.Printf("a[2] address is %p\n",&a[2]) //0xc000078040// 數組的遍歷方式for i:=0;i<len(a);i++{fmt.Println(a[i])}// for range 方式 for k,v := range a {fmt.Println(a[k])fmt.Println(v) // 這個值是元素的副本, 所以不建議在v 上操作: 取址或賦值; } }
---
數組有多種初始化方式: import "fmt"func main(){// 1. 字面量直接賦值var a [5]int = [5]int{1,2,3,4,5}var b = [5]int{6,7,8,9,0}// 2. 數組長度由編譯器推導var c = [...]int{1,2,3,4,5,6}// 3. 根據索引位置直接賦值var d = [...]int{1:1,10:0,3:3}// 4. 類型推導f := [...]int{1:1,9:9,10:0}fmt.Println(a)fmt.Println(b)fmt.Println(c)fmt.Println(d)fmt.Println(f) }
---
數組使用注意事項
1. 數組是多個相同類型的集合,一旦聲明或者定義了,數組的長度將是固定不變的,不能動態變化 ;
2. 數組中的元素可以是任意類型,可以是值類型,也可以是引用類型,不可以混用;
3. 數組創建后如果不賦值,將采用類型的零值 ;
4. 數組屬于值類型, 在默認情況下是傳遞數組是值傳遞,所以數組之間不會影響;
5. 如果需要更改原數組,需要傳遞數組的地址(類似引用傳遞);
6. 長度是數組類型的一部分,在傳遞參數時需要注意數組的長度; package mainimport "fmt"func test01(a [5]int){a[1] = 20fmt.Println(a) //[1 20 3 4 5] } func test02(a *[5]int){a[1] = 20fmt.Println(*a) // [1 20 3 4 5] } func main(){var a = [5]int{1,2,3,4,5}test01(a)fmt.Println(a) // [1 2 3 4 5]// 如果需要修改原數組的值,需要傳遞數組的地址test02(&a)fmt.Println(a) // [1 20 3 4 5]// 傳遞參數時需要考慮數組的長度//var b = [3]int{1,2,3}//test01(b) // 錯誤, 數組的長度不正確 }
在Go 編程中,數組的使用還是不太多, 因為不能動態的擴容,數組一般作為已經確定的數據使用;
##### 切片
1. 切片是數組的一個引用, 所以切片屬于引用類型,在進行參數傳遞時,屬于引用傳遞;
2. 切片的使用與數組類似: 遍歷,訪問等;
3. 切片的長度是可以動態變化的,所以也可以說切片是動態變化的數組;
4. 切片的基本語法 :
var 切片名 []類型 package mainimport "fmt"func main(){// 這里只演示切片如何使用,稍后再介紹切片如何聲明與賦值操作// 聲明并定義一個數組var a = [5]int{1,2,3,4,5}// 切片s 引用已經存在的數組avar s = a[:] // 這種方式引用所有的數組元素,也可引用一部分數組元素 var s = a[1:3]fmt.Printf("%T\n",s)fmt.Println(s)fmt.Println(len(s)) // 5 切片的長度fmt.Println(cap(s)) // 5 切片的容量,容量和長度是可以動態變化; }
---
切片在內存中的布局
1. slice 是一下引用類型;
2. 從底層上講,slice 實際上就是一個結構體(稍后會學習到)
type slice struct {
ptr *[2]int
len int
cap int
}
切片的初始化 package mainimport "fmt"func main(){// 方式1 創建一個數組,讓切片引用這個數組var a = [5]int{1,2,3,4,5}var s = a[:]// 同數組一樣, 在賦值或取值時,仍然需要注意切片下標索引不可越界//s[10] = 0s[4] = 50fmt.Println(s)// 方式2// 切片是引用類型,所以聲明后需要make 分配內存var b []int// make 分配內存,長度可以指定任意值,建議根據要存儲的數據長度合適定義// make 參數,還可以定義切片的容量,如果定義了切片的容量,則要求容量大于切片的長度b = make([]int,5)fmt.Println(b)// 方式3// 字面量方式,聲明后直接賦值var c = []int{1,2,3,4,5}fmt.Println(c) }
1. 方式1: 直接引用已經存在的數組,開發者對底層是可見的;
2. 方式2: 通過make 創建的切片,程序也會創建一個數組,不過這個數組對開發者不可見,由程序自己維護;
3. 方式3: 類似make;
---
切片使用的注意事項:
1. 切片的遍歷與訪問同數組一樣, 這里就不寫了;
2. 切片定義后,不能直接使用,需要引用一個數組或make 分配內存給切片使用;
3. 使用append 函數可以對切片進行動態擴容;
4. 使用copy 函數可以對切片進行拷貝,拷貝后的切片是獨立的, 在新的切片上操作對原切片沒有影響;
5. 切片是引用類型,在給函數傳遞參數(切片)時,屬于引用傳遞; package mainimport "fmt"func test01(a []int){a[0] = 100fmt.Println(a) } func main(){var a = []int{1,2,3}fmt.Println(a)// 使用append 函數將切片擴容a = append(a,4)fmt.Println(a)// 使用copy 函數進行切片復制// 在使用copy 函數時需要注意以下幾點://1. 目標(dst)必須是已經聲明并初始化過的//2. 如果目標的長度小于被復制的切片長度,則以目標的長度為準復制,超出部分不復制;var b []intb = make([]int,3)// 超出元素4,將不會被復制copy(b,a)fmt.Println(b)// 對新切片的操作不會影響到原來的切片b[1] = 10fmt.Println(a)fmt.Println(b)// 切片屬于引用傳遞test01(a)fmt.Println(a) }
---
string 與slice
在學習基本數據類型時我們學習了string 類型,知道它是由多個字節連接在一起的字符串;
實際上string 底層是一個字節數組,所以string 也可以進行切片操作;
但是string 是不可變的, 所以不能string[0]='x', 這種賦值操作; 如果需要更改,需要將string 轉為[]byte 切片,更改完成后再轉為string 類型; package mainimport "fmt"func main(){var a = "abcdef"// 可以進行切片處理fmt.Println(a[:3])// 不可以更改string 的值// a[1] = 'f' //這是錯誤的// 需要將string 轉為 []byte 切片才可以操作var arr []bytearr = []byte(a)arr[1] = 'f'a = string(arr) // 更改完成后再將切片轉為string 類型fmt.Println(a)// []byte 只能處理英文與數字,處理其它語言時需要將string 轉為[]rune 切片var b = "你好啊"var arr2 []runearr2 = []rune(b)arr2[0] = '我'b = string(arr2)fmt.Println(b) }
***渡風渡塵緣,修行亦修心***
##### 數組
數組是可以存放多個同一類型的數據的集合,數組也是一種數據類型,它是值類型;
數組如何定義?
var 數組名 [數組大小]數據類型: var a [5]int package mainimport "fmt"func main(){var intArr [3]intintArr[0] = 10intArr[1] = 20intArr[2] = 30fmt.Println(intArr) }
數組在內存中的布局:
1. 數組的地址可以通過數組名獲取: &intArr;
2. 數組的第一個元素地址就是數組的地址:;
3. 數組的各個元素的地址間隔根據數組的類型決定: int64-> 8 字節,int32-> 4字節;
4. 數組如果未給元素賦值,元素的默認值為類型的零值: int64->0,string->"",float64->0;
5. 訪問數組中的元素: 使用下標索引方式(左閉右開方式),這個與其它語言一樣,超出下標索引將會出錯,索引越界;
6. 數組的遍歷有兩種方式: for 傳統方式; for range 方式 ; package mainimport "fmt"func main(){var a [5]inta[0] = 1a[1] = 2a[2] = 3// 默認值: a[3] = 0 , a[4] = 0// 下標越界: a[5] = 10 // 錯誤, 下標越界fmt.Println(a) //[1 2 3 0 0 ]fmt.Printf("a address is %p\n",&a) // 0xc000078030fmt.Printf("a[0] address is %p\n",&a[0]) //0xc000078030fmt.Printf("a[1] address is %p\n",&a[1]) //0xc000078038fmt.Printf("a[2] address is %p\n",&a[2]) //0xc000078040// 數組的遍歷方式for i:=0;i<len(a);i++{fmt.Println(a[i])}// for range 方式 for k,v := range a {fmt.Println(a[k])fmt.Println(v) // 這個值是元素的副本, 所以不建議在v 上操作: 取址或賦值; } }
---
數組有多種初始化方式: import "fmt"func main(){// 1. 字面量直接賦值var a [5]int = [5]int{1,2,3,4,5}var b = [5]int{6,7,8,9,0}// 2. 數組長度由編譯器推導var c = [...]int{1,2,3,4,5,6}// 3. 根據索引位置直接賦值var d = [...]int{1:1,10:0,3:3}// 4. 類型推導f := [...]int{1:1,9:9,10:0}fmt.Println(a)fmt.Println(b)fmt.Println(c)fmt.Println(d)fmt.Println(f) }
---
數組使用注意事項
1. 數組是多個相同類型的集合,一旦聲明或者定義了,數組的長度將是固定不變的,不能動態變化 ;
2. 數組中的元素可以是任意類型,可以是值類型,也可以是引用類型,不可以混用;
3. 數組創建后如果不賦值,將采用類型的零值 ;
4. 數組屬于值類型, 在默認情況下是傳遞數組是值傳遞,所以數組之間不會影響;
5. 如果需要更改原數組,需要傳遞數組的地址(類似引用傳遞);
6. 長度是數組類型的一部分,在傳遞參數時需要注意數組的長度; package mainimport "fmt"func test01(a [5]int){a[1] = 20fmt.Println(a) //[1 20 3 4 5] } func test02(a *[5]int){a[1] = 20fmt.Println(*a) // [1 20 3 4 5] } func main(){var a = [5]int{1,2,3,4,5}test01(a)fmt.Println(a) // [1 2 3 4 5]// 如果需要修改原數組的值,需要傳遞數組的地址test02(&a)fmt.Println(a) // [1 20 3 4 5]// 傳遞參數時需要考慮數組的長度//var b = [3]int{1,2,3}//test01(b) // 錯誤, 數組的長度不正確 }
在Go 編程中,數組的使用還是不太多, 因為不能動態的擴容,數組一般作為已經確定的數據使用;
##### 切片
1. 切片是數組的一個引用, 所以切片屬于引用類型,在進行參數傳遞時,屬于引用傳遞;
2. 切片的使用與數組類似: 遍歷,訪問等;
3. 切片的長度是可以動態變化的,所以也可以說切片是動態變化的數組;
4. 切片的基本語法 :
var 切片名 []類型 package mainimport "fmt"func main(){// 這里只演示切片如何使用,稍后再介紹切片如何聲明與賦值操作// 聲明并定義一個數組var a = [5]int{1,2,3,4,5}// 切片s 引用已經存在的數組avar s = a[:] // 這種方式引用所有的數組元素,也可引用一部分數組元素 var s = a[1:3]fmt.Printf("%T\n",s)fmt.Println(s)fmt.Println(len(s)) // 5 切片的長度fmt.Println(cap(s)) // 5 切片的容量,容量和長度是可以動態變化; }
---
切片在內存中的布局
1. slice 是一下引用類型;
2. 從底層上講,slice 實際上就是一個結構體(稍后會學習到)
type slice struct {
ptr *[2]int
len int
cap int
}
切片的初始化 package mainimport "fmt"func main(){// 方式1 創建一個數組,讓切片引用這個數組var a = [5]int{1,2,3,4,5}var s = a[:]// 同數組一樣, 在賦值或取值時,仍然需要注意切片下標索引不可越界//s[10] = 0s[4] = 50fmt.Println(s)// 方式2// 切片是引用類型,所以聲明后需要make 分配內存var b []int// make 分配內存,長度可以指定任意值,建議根據要存儲的數據長度合適定義// make 參數,還可以定義切片的容量,如果定義了切片的容量,則要求容量大于切片的長度b = make([]int,5)fmt.Println(b)// 方式3// 字面量方式,聲明后直接賦值var c = []int{1,2,3,4,5}fmt.Println(c) }
1. 方式1: 直接引用已經存在的數組,開發者對底層是可見的;
2. 方式2: 通過make 創建的切片,程序也會創建一個數組,不過這個數組對開發者不可見,由程序自己維護;
3. 方式3: 類似make;
---
切片使用的注意事項:
1. 切片的遍歷與訪問同數組一樣, 這里就不寫了;
2. 切片定義后,不能直接使用,需要引用一個數組或make 分配內存給切片使用;
3. 使用append 函數可以對切片進行動態擴容;
4. 使用copy 函數可以對切片進行拷貝,拷貝后的切片是獨立的, 在新的切片上操作對原切片沒有影響;
5. 切片是引用類型,在給函數傳遞參數(切片)時,屬于引用傳遞; package mainimport "fmt"func test01(a []int){a[0] = 100fmt.Println(a) } func main(){var a = []int{1,2,3}fmt.Println(a)// 使用append 函數將切片擴容a = append(a,4)fmt.Println(a)// 使用copy 函數進行切片復制// 在使用copy 函數時需要注意以下幾點://1. 目標(dst)必須是已經聲明并初始化過的//2. 如果目標的長度小于被復制的切片長度,則以目標的長度為準復制,超出部分不復制;var b []intb = make([]int,3)// 超出元素4,將不會被復制copy(b,a)fmt.Println(b)// 對新切片的操作不會影響到原來的切片b[1] = 10fmt.Println(a)fmt.Println(b)// 切片屬于引用傳遞test01(a)fmt.Println(a) }
---
string 與slice
在學習基本數據類型時我們學習了string 類型,知道它是由多個字節連接在一起的字符串;
實際上string 底層是一個字節數組,所以string 也可以進行切片操作;
但是string 是不可變的, 所以不能string[0]='x', 這種賦值操作; 如果需要更改,需要將string 轉為[]byte 切片,更改完成后再轉為string 類型; package mainimport "fmt"func main(){var a = "abcdef"// 可以進行切片處理fmt.Println(a[:3])// 不可以更改string 的值// a[1] = 'f' //這是錯誤的// 需要將string 轉為 []byte 切片才可以操作var arr []bytearr = []byte(a)arr[1] = 'f'a = string(arr) // 更改完成后再將切片轉為string 類型fmt.Println(a)// []byte 只能處理英文與數字,處理其它語言時需要將string 轉為[]rune 切片var b = "你好啊"var arr2 []runearr2 = []rune(b)arr2[0] = '我'b = string(arr2)fmt.Println(b) }
個人微信公眾號上有最新的文章,歡迎大家關注,一同交流學習
轉載于:https://www.cnblogs.com/Mail-maomao/p/11411807.html
總結
- 上一篇: Go 常用函数
- 下一篇: http预请求options