Go語言數(shù)組數(shù)組
一維數(shù)組
- 數(shù)組定義格式:
var arr [3]int
- 數(shù)組的初始化方式
- 先定義后初始化
注意點(diǎn):
Go語言數(shù)組支持先定義后一次性初始化
//1.先定義后初始化 var nums [3]int //先定義數(shù)組再逐個(gè)初始化 nums[0] = 1 nums[1] = 2 nums[2] = 3 //先定義數(shù)組再一次性初始化 nums = [3]int{2,3,4} fmt.Println(nums)
- 定義的同時(shí)初始化
-
注意點(diǎn):
給數(shù)組一次性賦值的時(shí)候一定要在值的前面加上數(shù)組的類型,而且必須要和數(shù)據(jù)類型一模一樣 - C語言中數(shù)組沒有初始化保存的是垃圾數(shù)據(jù),Go語言中數(shù)組沒有初始化保存的是零值
-
//var arr [3]int = [3]int{2, 3, 4} //定義的同時(shí)部分初始化,未初始話的元素為0 var arr [3]int = [3]int{1,3} //指定元素初始化 var arr [3]int = [3]int{2:4} //[...]的含義, 讓編譯器根據(jù)賦值的元素個(gè)數(shù)自動計(jì)算數(shù)組的元素個(gè)數(shù) arr := [...]int{1,3,4} fmt.Println(arr) fmt.Println(arr[0]) fmt.Println(arr[1]) fmt.Println(arr[2])
- 先定義后初始化
零值
- 什么是零值
- 數(shù)據(jù)類型未初始化,系統(tǒng)自動添加的數(shù)據(jù)
- 各種數(shù)據(jù)類型零值
- int/int8/int16/int32/int64/uint/uint8/uint16/uint32/uint64/byte/rune/uintptr的默認(rèn)值是0
- float32/float64的默認(rèn)值是0.0
- bool的默認(rèn)值是false
- string的默認(rèn)值是""
- pointer/function/interface/slice/channel/map/error的默認(rèn)值是nil
值類型
- Go語言在數(shù)組中是值類型,所以數(shù)組之間的賦值是值拷貝關(guān)系,而不是指向關(guān)系
- 如果想讓兩個(gè)數(shù)組可以相互賦值, 那么兩個(gè)數(shù)組的類型必須一致類型一致包括元素個(gè)數(shù), 元素個(gè)數(shù)也是Go語言中數(shù)組類型的一部分
- 如果數(shù)組中存儲的元素類型支持== != 操作,而且兩個(gè)數(shù)組的類型是一個(gè)樣的, 那么數(shù)組也支持== !=操作
package main
import "fmt"
func main() {
nums := [...]int {2,3,4}
arr := [...]int{2,3,4}
fmt.Println(nums == arr) //true
nums := [3]int{1,2,3}
res := nums
fmt.Println(res) //[1,2,3]
nums := [3]int{1,2,3}
change(nums)
fmt.Println(nums) //[1,2,3]
}
//函數(shù)內(nèi)部無法改變數(shù)組元素的值
func change(nums [3]int) {
nums[2] = 666
}
二維數(shù)組
- Go語言二維數(shù)組格式:
var 數(shù)組名稱[一維數(shù)組個(gè)數(shù)][一維數(shù)組元素個(gè)數(shù)]數(shù)組類型
package main
func main() {
//定義二維數(shù)組格式
var nums [2][3]int = [2][3]int{
{1,3,5},
{2,5,6},
}
//簡單定義方法
nums := [2][3]int{
{1,2,3},
{4,5,6},
}
arr := [...][3]int{
{12,3,5},
{1,2,6},
}
fmt.Println(arr)
//二維數(shù)組也可以省略元素個(gè)數(shù),但是只可以省略行數(shù),不可以省略列數(shù)
arr := [...][3]int{
{12,3,5},
{1,2,6},
}
//遍歷二維數(shù)組
for i:=0; i< 2; i++{
for j:=0;j < 3 ;j++ {
fmt.Println(arr[i][j])
}
}
}
數(shù)組的遍歷
- Go語言數(shù)組遍歷兩種方法
- 普通for循環(huán)遍歷
nums := [...]int{2, 3, 5, 1, 3, 6} //普通for循環(huán)遍歷 for i := 0; i < 6; i++ { fmt.Println("index= ", i, "value = ", nums[i]) }
- for..range遍歷
nums := [...]int{2, 3, 5, 1, 3, 6} //高級for循環(huán)遍歷 for key,value := range nums{ fmt.Println(key,"-->",value) }
Go語言切片
切片
-
什么是切片
- 在Go語言中,數(shù)組的長度一旦確定就無法改變,為了解決這一問題,推出了一種數(shù)據(jù)類型切片
- 切片簡單理解就是一個(gè)可變長度的數(shù)組,底層的實(shí)現(xiàn)原理就是一個(gè)結(jié)構(gòu)體, 結(jié)構(gòu)體中有一個(gè)指針指向了一個(gè)數(shù)組
本質(zhì)上所有的數(shù)據(jù)都是保存在指向的數(shù)組中的
-
創(chuàng)建切片
- 通過數(shù)組創(chuàng)建切片1
通過數(shù)組來創(chuàng)建 格式: [起始位置:結(jié)束位置], 從起始位置開始截取, 直到結(jié)束位置, 但是不包括結(jié)束位置 注意: 截取了多少個(gè)元素, len就等于幾 容量等于數(shù)組的長度 - 起始位置 nums := [5]int{1,2,3,4,5} //1.通過數(shù)組創(chuàng)建切片 //1.1[起始位置 : 結(jié)束位置] 從起始位置開始截取,直到結(jié)束位置,但是不包括結(jié)束位置索引對應(yīng)的元素 var sce []int = nums[2 : 4] //1.2[: 結(jié)束位置]只指定結(jié)束位置,不指定起始位置,就是從開始位置截取直到指定的位置,但是也不包括指定位置的元素 var sce []int = nums[:3] //1.3[起始位置 : ] 從起始位置開始截取,直到末尾 var sce []int = nums[0:] //1.4[: ]只有一個(gè):,從開始位置截取,直到末尾結(jié)束 var sce []int = nums[:] fmt.Println(sce) //計(jì)算切片的長度len當(dāng)前保存的數(shù)據(jù)個(gè)數(shù) fmt.Println(len(sce)) //計(jì)算切片的容量cap總共能夠保存數(shù)據(jù)的個(gè)數(shù) fmt.Println(cap(sce))
- 通過數(shù)組創(chuàng)建切片2
切片的第三個(gè)參數(shù)max,不能小于第二個(gè)參數(shù),作用是用來控制切片容量的
//如果指定了第三個(gè)參數(shù), 那么切片的容量就等于 第三個(gè)參數(shù) - 第一個(gè)參數(shù) //注意點(diǎn): 如果沒有指定第三個(gè)參數(shù),那么切片的容量 = 數(shù)組的容量 - 第一個(gè)參數(shù)值 // 如果指定了第三個(gè)參數(shù),那么切片的容量 = 第三個(gè)參數(shù) - 第一個(gè)參數(shù)值 arr := [7]int{1,2,3,4,5,6,7} sce := arr[1:4:4] fmt.Println(sce) fmt.Println(len(sce)) fmt.Println(cap(sce))
- 通過make()函數(shù)創(chuàng)建切片
// 2.通過make函數(shù)創(chuàng)建切片 // 第一個(gè)參數(shù): 告訴系統(tǒng)要存儲什么類型的數(shù)據(jù) // 注意點(diǎn): 如果是創(chuàng)建切片一定要在傳入的數(shù)據(jù)類型前面寫上[] // 第二個(gè)參數(shù): 告訴系統(tǒng)創(chuàng)建出來的切片len等于多少 // 第三個(gè)參數(shù): 告訴系統(tǒng)創(chuàng)建出來的切片cap等于多少 // 注意點(diǎn): 第三個(gè)參數(shù)可以省略, 如果省略切片的容量就等于切片的長度(等于第二個(gè)參數(shù)) //var sce []int = make([]int, 2, 5) var sce []int = make([]int, 2) fmt.Println(sce) fmt.Println(len(sce)) fmt.Println(cap(sce))
- 通過語法糖創(chuàng)建切片
// 3.通過Go提供的語法糖來創(chuàng)建 // 一定要注意[]里面沒有值就是切片 // 通過Go提供的語法糖來創(chuàng)建len等于cap var sce []int = []int{1, 3, 5} // 相當(dāng)于make([]int, 3) fmt.Println(sce) fmt.Println(len(sce)) fmt.Println(cap(sce))
切片與數(shù)組
- [ ]沒有數(shù)字就是切片,有數(shù)字就是數(shù)組,[...]這種定義格式是數(shù)組
//定義數(shù)組
var arr [5]int = [5]int{1,3,4,5,6}
var arr = [5]int{1,3,4,5,6}
arr := [5]int{1,3,4,5,6}
arr := [...]int{1,3,4,5,6}
//定義二維數(shù)組
var arrs [2][3]int = [2][3]int{
{1,3,5},
{2,4,6},
}
var arrs = [2][3]int{
{1,3,5},
{2,4,6},
}
arrs := [2][3]int{
{1,3,5},
{2,4,6},
}
arrs := [...][3]int{
{1,3,5},
{2,4,6},
}
//定義切片
var sce []int = []int{1,3,5}
var sce = []int{1,3,5}
sce := []int{1,3,5}
//定義一個(gè)切片,切片中保存的是數(shù)組
var sec [][3]int = [][3]int{
{1, 3, 5},
{2, 4, 6},
}
var sec = [][3]int{
{1, 3, 5},
{2, 4, 6},
}
sec := [][3]int{
{1, 3, 5},
{2, 4, 6},
}
//定義一個(gè)切片,切片中保存切片
var sec[][]int = [][]int {
{1, 3, 5},
{2, 4, 6},
}
var sec = [][]int {
{1, 3, 5},
{2, 4, 6},
}
sec := [][]int {
{1, 3, 5},
{2, 4, 6},
}
append函數(shù)
- append函數(shù)格式 :
append(切片,數(shù)據(jù))
- append函數(shù)作用: 追加切片中的數(shù)據(jù)
- append函數(shù)注意點(diǎn)
- 如果通過append函數(shù)追加數(shù)據(jù)之后超過了原有的容量, 那么系統(tǒng)內(nèi)部會自動按照當(dāng)前容量*2的方式重新定義一個(gè)數(shù)組作為切片保存數(shù)據(jù)的模型
- 在擴(kuò)容的時(shí)候會重新定義一個(gè)新的切片, 所以需要返回一個(gè)切片
- 擴(kuò)容問題
- 容量小于1024的時(shí)候擴(kuò)容, 會按照原有容量的2倍擴(kuò)容
- 容量大于1024的時(shí)候擴(kuò)容,會按照原有的1/4進(jìn)行擴(kuò)容
sec := make([]int, 1024, 1024)
sec = append(sec, 6)
fmt.Println(len(sec))
fmt.Println(cap(sec))//1280
切片的使用
- 定義切片
sec := []int{1,3,5,7,9,11}
- 修改切片中的數(shù)據(jù)
sec[2] = 666
- 增加切片中的數(shù)據(jù)
sec = append(sec, 7)
fmt.Println(sec)
- 刪除切片中指定的數(shù)據(jù)
//5.刪除切片中的指定索引對應(yīng)的數(shù)據(jù)
index := 3
//不僅可以利用數(shù)組獲取切片,還可以用切片獲取切片
//sec = sec[:index]
//fmt.Println(sec) //[1 3 5]
//sec = sec[index +1 :]
//fmt.Println(sec) //[9 11]
//將第二個(gè)切片追加到第一個(gè)切片后面即可
sec = append(sec[:index], sec[index +1 :]...)
fmt.Println(sec) //[1 3 5 9 11]
切片的內(nèi)存地址
package main
import "fmt"
func main() {
/*
通過切片生成切片的注意點(diǎn)
*/
//1.創(chuàng)建一個(gè)數(shù)組
var arr = [3]int{1,2,3}
//Go語言中不能通過數(shù)組名稱獲取數(shù)組的地址
//fmt.Printf("%p\n", arr)
fmt.Printf("%p\n", &arr) //0xc0420500a0
fmt.Printf("%p\n", &arr[0]) //0xc0420500a0
//2.通過數(shù)組創(chuàng)建一個(gè)切片
//直接打印sce打印的是sce中指針保存的地址,也就是底層指向的那個(gè)數(shù)組的地址
sce := arr[:]
fmt.Printf("%p\n", sce) //0xc0420500a0
//3.通過切片創(chuàng)建切片
//通過切片創(chuàng)建一個(gè)切片, 新的切片和老的切片底層指向同一個(gè)數(shù)組
sce2 := sce[:]
fmt.Printf("%p\n", sce2) //0xc0420500a0
//修改數(shù)組中的值
arr[2] = 666
sce[2] = 666
sce2[2] = 666
fmt.Println(arr)
fmt.Println(sce)
fmt.Println(sce2)
}
copy函數(shù)
package main
import "fmt"
func main() {
/*
copy函數(shù)的作用是將源切片的值拷貝到目標(biāo)切片當(dāng)中
函數(shù)格式 copy(目標(biāo)切片, 源切片)
*/
sec1 := []int{1, 2, 3, 6, 9, 10}
sec2 := make([]int, 2, 5)
fmt.Println(sec2)
//將源切片的值拷貝到目標(biāo)切片當(dāng)中
//注意點(diǎn): 拷貝的時(shí)候是以目標(biāo)切片為準(zhǔn),目標(biāo)切片中長度為多少將來只能拷貝多少個(gè)值進(jìn)來
copy(sec2, sec1)
fmt.Println(sec2)
}
切片與字符串
注意點(diǎn): 切片和數(shù)組不同, 切片不支持== !=操作
package main
import "fmt"
func main() {
/*
1.切片可以再生成新的切片, 兩個(gè)切片底層指向同一個(gè)數(shù)組
2.切片和數(shù)組不同, 切片不支持== !=操作
3.在Go語言中字符串的底層實(shí)現(xiàn)就是切片, 所以可以通過字符串來生成切片
*/
//數(shù)組可以使用== !=操作
//arr := [3]int{1,2,3}
//arr2 := [3]int{4,5,6}
//arr = arr2
//fmt.Println(arr)
var str string = "www.it666.com"
sec := make([]byte, len(str))
copy(sec,str)
fmt.Println(len(str))
fmt.Printf("%s\n",sec) //www.it666.com
}
切片與數(shù)組注意點(diǎn)
數(shù)組定義后不初始化,可以使用,切片定義后可以一次性初始化,但是不能逐個(gè)初始化,但是可以使用append函數(shù)添加數(shù)據(jù)
- 數(shù)組定義后直接使用
var arr [4]int
//數(shù)組定義后可以直接使用
arr[0] = 1
arr[1] = 3
arr[2] = 5
arr[3] = 7
fmt.Println(arr)
- 切片定義后不能直接使用
var sce []int
//不能逐個(gè)初始化,會報(bào)錯
//sce[0] = 1
//sce[1] = 3
//sce[2] = 5
//可以使用append函數(shù)添加數(shù)據(jù)
sce = append(sce,1)
sce = append(sce,3)
sce = append(sce,5)
sce = append(sce,7)
sce = append(sce,9)
//打印切片
fmt.Println(sce)
fmt.Println(len(sce))
fmt.Println(cap(sce))
- 計(jì)算數(shù)組的長度方式
//計(jì)算數(shù)組長度的方式
//第一種方式(推薦)使用len函數(shù)
arr := [4]int{1,3,5,7}
//length := len(arr)
//fmt.Println(length)
//第二種方式,使用unsafe.Sizeof函數(shù)計(jì)算(不推薦)
length1 := unsafe.Sizeof(arr)
length2 := unsafe.Sizeof(arr[0])
fmt.Println(length1 / length2)
- 計(jì)算切片的長度
//計(jì)算切片的長度
//第一種方式(推薦)使用len函數(shù)
//注意點(diǎn): 切片的長度不能使用unsafe.Sizeof函數(shù)計(jì)算
//由于切片本質(zhì)是結(jié)構(gòu)體,所以unsafe.Sizeof計(jì)算的是結(jié)構(gòu)體的字節(jié)數(shù),不是指向數(shù)組的字節(jié)數(shù)
sce := []int{1,3,5,6}
length := len(sce)
fmt.Println(length)
- 切片與數(shù)組的傳遞
- 數(shù)組傳遞是值傳遞
- 切片傳遞是地址(指針)傳遞
package main
import "fmt"
func main() {
/*
1.數(shù)組作為函數(shù)參數(shù)是值傳遞,在函數(shù)內(nèi)部修改形參是不會影響外部實(shí)參的值
2.切片作為函數(shù)的參數(shù)是地址傳遞(指針), 在函數(shù)內(nèi)修改形參, 會影響到函數(shù)外的實(shí)參
*/
//arr := [4]int{1,3,4,5}
//fmt.Println(arr)
//change1(arr)
//fmt.Println(arr)
sce := []int{1,2,3,4}
fmt.Println(sce)
change2(sce)
fmt.Println(sce)
}
//數(shù)組是值傳遞
func change1(arr [4]int) {
arr[0] = 666
}
//切片是地址傳遞指針
func change2(sce []int) {
sce[0] = 666
}
可變參數(shù)
- 可變參數(shù)底層實(shí)現(xiàn)就是切片
package main
import "fmt"
func main() {
/*
可變參數(shù),底層其實(shí)就是一個(gè)切片
*/
res := sum(10,20,30)
fmt.Println(res)
}
//可變參數(shù)底層就是一個(gè)切片
//注意點(diǎn): 當(dāng)一個(gè)函數(shù)中定義可變參數(shù)時(shí)候,可變參數(shù)只能放在形參列表的最后面
func sum(nums ...int) (res int) {
for _, value := range nums {
res += value
}
return
}