七、数组与切片
Go 中,数组是值类型
var hens [7]float64
hens[0] = 3.0
hens[1] = 3.0
hens[2] = 3.0
hens[3] = 3.0
hens[4] = 3.0
totalWeight := 0.0
for i := 0; i < len(hens); i++ {
totalWeight += hens[i]
}
avgWeight := fmt.Sprintf("%.2f", totalWeight/float64(len(hens)))
fmt.Printf("total =%v,avg=%v", totalWeight, avgWeight)
var 数组名 [数组大小]数据类型
var class [50]int
var class [3]int = [...]int{1,2,3}
class[0]=1
class[1]=2
数组的内存布局#
当我们定义完数组后,其实数组的各个元素有默认值
- 数组的地址可以通过数组名来获取 &intArr
- 数组的地址就是首位元素的地址
- 数组的各个元素的地址间隔是依据数组的类型决定 比如 int64->8 int32->4
var intArr [3]int
fmt.Println(intArr)
//[0 0 0]
fmt.Println(&intArr[0])
//0xc000010360
var intArr [3]int
intArr[0] = 10
intArr[1] = 20
intArr[2] = 20
fmt.Printf("intArr的地址=%p intArr[0] 地址%p intArr[1] 地址%p intArr[2] 地址%p", &intArr, &intArr[0], &intArr[1], &intArr[2])
//intArr的地址=0xc000010360 intArr[0] 地址0xc000010360 intArr[1] 地址0xc000010368 intArr[2] 地址0xc000010370
数组名 [下标]
var arr [5]float64
for i := 0; i < len(arr); i++ {
fmt.Printf("请输入第%d个元素的值\n", i+1)
fmt.Scanln(&arr[i])
}
for i := 0; i < len(arr); i++ {
fmt.Printf("arr[%d]=%v", i, arr[i])
}
四种初始化数组的方法#
var numarr01 [3]int = [3]int{1, 2, 3}
fmt.Println("numarr01=", numarr01)
var numarr02 = [3]int{1, 2, 3}
fmt.Println("numarr02=", numarr02)
var numarr03 = [...]int{8, 9, 10}
fmt.Println("numarr03=", numarr03)
var numarr04 = [...]int{1: 800, 2: 129, 3: 10}
fmt.Println("numarr03=", numarr04)
//类型推导
strArr := [...]string{1: "tom", 2: "jack", 3: "zjm"}
fmt.Println("strArr=", strArr)
//numarr01= [1 2 3]
//numarr02= [1 2 3]
//numarr03= [8 9 10]
//numarr03= [0 800 129 10]
//strArr= [ tom jack zjm]
数组的遍历#
一般用 for 或者 for–range
heros := [...]string{"zjm", "whq", "xgd"}
for i, v := range heros {
fmt.Printf("i=%v v=%v\n", i, v)
fmt.Printf("heros[%d]=%v\n", i, heros[i])
}
for _, v := range heros {
fmt.Printf("元素的值%v\n", v)
}
使用注意 - 217#
- 数组是多个相同类型数据的组合,一个数组一旦什么了,其长度是固定的,不能动态改变
- Var arr [] int arr 就是一个切片
- 数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用
- 数组创建后,没有赋值,有默认值 (string :‘’ bool:false 数值型:0)
- 数组的下标必须在指定范围内使用,否则报 panic:数组越界
- Go 的数组属值类型,在默认情况下是值传递,因此会进行值拷贝,数组间不会相互影响
- 如果下在其他函数中,去修改原来的数组,可以使用引用传递(指针方式)
- 长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度
var arr [3]int
arr[0] = 1
arr[1] = 2
arr[2] = 3.1
arr[3] = 4
var arr1 [3]float32
var arr2 [3]string
var arr3 [3]bool
fmt.Printf("arr1=%v arr2=%v arr3=%v", arr1, arr2, arr3)
//arr1=[0 0 0] arr2=[ ] arr3=[false false false]
//Go的数组属值类型,在默认情况下是值传递,因此会进行值拷贝,数组间不会相互影响
注意是值拷贝,所以呢,这个是在栈里新建一个数组
arr := [3]int{11, 22, 33}
test(arr)
fmt.Println(arr)
func test(arr [3]int) {
arr[0] = 88
fmt.Println("test中的arr:", arr)
}
test中的arr: [88 22 33]
[11 22 33]
要改的话,需要用的是指针,改变地址引用
arr := [3]int{11, 22, 33}
test01(arr)
test02(&arr) //传地址
fmt.Println("arr:", arr)
func test01(arr [3]int) { //go中数组的长度是类型的一部分 [3]int
arr[0] = 88
fmt.Println("test01中的arr:", arr)
}
func test02(arr *[3]int) {
(*arr)[0] = 88 //指针获取到地址
fmt.Println("test02中的arr:", arr)
}
test01中的arr: [88 22 33]
test02中的arr: &[88 22 33]
arr: [88 22 33]
长度问题
var age [...]{1,2,3} //长度3
var age [] //长度0 函数参数传递的时候不能把3传0
var age [4]int //长度3不能传4,要保持一致
var age [3]{3,4,5}
数组的应用案例#
var chars [26]byte
for i := 0; i < 26; i++ {
chars[i] = 'A' + byte(i)
}
for i := 0; i < 26; i++ {
fmt.Printf("%c", chars[i])
}
//ABCDEFGHIJKLMNOPQRSTUVWXYZ
var intArr [6]int = [...]int{1, -1, 9, 99, 999, 9999} //声明
maxVal := intArr[0]
maxIndex := 0
for i := 0; i < len(intArr); i++ {
for maxVal < intArr[i] {
maxVal = intArr[i]
maxIndex = i
}
}
fmt.Printf("maxVal=%v , maxIndex=%v", maxVal, maxIndex)
//maxVal=9999 , maxIndex=5
var intArr [5]int = [...]int{1, 9, 3, 4, 5}
sum := 0
for _, v := range intArr {
sum += v
}
fmt.Printf("sum=%v,avg=%v", sum, float64(sum)/float64(len(intArr)))
//都是int
var intArr3 [5]int
len := len(intArr3)
rand.Seed(time.Now().UnixNano())
for i := 0; i < len; i++ {
intArr3[i] = rand.Intn(100)
}
fmt.Println("交换前=", intArr3)
temp := 0
for i := 0; i < len/2; i++ {
temp = intArr3[len-1-i]
intArr3[len-1-i] = intArr3[i]
intArr3[i] = temp
}
fmt.Println("交换后=", intArr3)
交换前= [24 63 71 32 66]
交换后= [66 32 71 63 24]
切片#
为什么要切片?在元素个数不确定的情况下,使用数组不知道开多大,大了浪费小了不够用
切片英文 slice
切片是数组的一个引用,在进行传递时,遵守引用传递机制 【】
切片的使用和数组基本相似(遍历、访问、求长度等)
切片的长度是可以变化的,动态变化
定义: var 切片名 [] 类型 比如: var age [] int
切片的使用#
var intArr [5]int = [...]int{1, 22, 33, 66, 99}
//slice := intArr[1:4] //表示slice 应用到intArr这个数组
//intArr[1:3],起始下标包1不包3
slice := intArr[1:4]
fmt.Println("intArr=", intArr)
fmt.Println("slice元素是", slice)
fmt.Println("slice元素长度", len(slice))
fmt.Println("slice元素容量", cap(slice))
intArr= [1 22 33 66 99]
slice元素是 [22 33 66]
slice元素长度 3
slice元素容量 4
fmt.Printf("intArr[1]的地址=%p\n", &intArr[1])
fmt.Printf("intArr[0]的地址=%p slice[0]==%v\n", &slice[0], slice[0])
intArr[1]的地址=0xc0000ca038
intArr[0]的地址=0xc0000ca038 slice[0]==22
切片的内存形式#
引用,包括地址、len 长度、cap 容量
//定义切片,引用数组
var arr [5]int = [...]int{1, 2, 3, 4, 5}
var slice = arr[1:3]
fmt.Println("arr=", arr)
fmt.Println("slice=", slice)
fmt.Println("slice len=", len(slice))
fmt.Println("slice cap=", cap(slice))
arr= [1 2 3 4 5]
slice= [2 3]
slice len= 2
slice cap= 4
make 创建#
Var 切片名 [] type = make ([] type,len,[cap]) ;cap 必须大于 len
var slice []float64 = make([]float64, 5, 10)
slice[1] = 10
slice[3] = 20
fmt.Println("slice=", slice)
fmt.Println("slice len=", len(slice))
fmt.Println("slice cap=", cap(slice))
slice= [0 10 0 20 0]
slice len= 5
slice cap= 10
总结:
- make 必须指定 cap 和 len
- 没有给切片元素复制,默认(int,float => 0 string=> “” bool=>false)
- make 方式创建的切片对应的数组由 make 底层维护不可见,只能通过 slice 去访问各个元素
- 方式 1 和方式 2 的区别:1 的数组可见 2 的数组不可见
定义一个切片时候指定数组#
var strSlice []string = []string{"tom", "jack", "rose"}
fmt.Println("strSlice=", strSlice)
fmt.Println("strSlice len=", len(strSlice))
fmt.Println("strSlice cap=", cap(strSlice))
strSlice= [tom jack rose]
strSlice len= 3
strSlice cap= 3
切片的遍历#
For 和 for–range
var strSlice []string = []string{"tom", "jack", "rose", "who"}
aaSlice := strSlice[0:4]
for i := 0; i < len(aaSlice); i++ {
fmt.Printf("aaSlice[%v]=%v\n", i, aaSlice[i])
}
for i, v := range aaSlice {
fmt.Printf("aaSlice[%v]=%v\n", i, v)
}
aaSlice[0]=tom
aaSlice[1]=jack
aaSlice[2]=rose
aaSlice[3]=who
切片的使用注意#
切片初始化 Var slice=[a:b]
cap 是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素
切片定义往后,还不能使用,因为切片本身是一个空的,需要让其应用到一个数组,或者 make 一个空间供切片来使用
切片可以继续切片
appen 动态追加,【slice3… 是自己】
vare slice = arr[0:end] var slice=arr[:end]
vare slice = arr[start:len(arr)] var slice=arr[start:]
vare slice = arr[0:len(arr)] var slice=arr[:]
注意:
var strSlice []string = []string{"tom", "jack", "rose", "who"}
slice2 := strSlice[1:3]
slice3 := strSlice[1:2]
slice3[0] = "zhang"
fmt.Println("slice2=", slice2)
fmt.Println("slice3", slice3)
fmt.Println("strSlice=", strSlice)
---
slice2= [zhang rose]
slice3 [zhang]
strSlice= [tom zhang rose who]
//因为指向的是同一个空间,所以后面改这个值也会影响
var slice3 []int = []int{11, 22, 33}
slice3 = append(slice3, 400, 500, 600)
fmt.Println("slice3", slice3)
slice3 = append(slice3, slice3...)
fmt.Println("slice3", slice3)
---
slice3 [11 22 33 400 500 600]
slice3 [11 22 33 400 500 600 11 22 33 400 500 600]
append 原理:
- append 其实就是扩容
- go 底层会创建一个新的数组 newArr(安装扩容后大小)
- 将 slice 原来包含的元素拷贝到新的数组 newArr
- slice 重新引用到 newArr,底层不可见
再次强调切片是引用类型,传递时候遵守引用传递机制:【本质是操作一个空间】
var slice []int
var arr [5]int = [...]int{1, 2, 3, 4, 5}
slice = arr[:]
var slice2 = slice
slice2[0] = 10
fmt.Println("slice2", slice2)
fmt.Println("slice", slice)
fmt.Println("arr", arr)
---
slice2 [10 2 3 4 5]
slice [10 2 3 4 5]
arr [10 2 3 4 5]
切片的拷贝操作#
切片使用 copy 内置函数完成拷贝
- copy (A,B), 把 B 的值赋值给 A,都是切片
- A,B 空间独立
- copy 长度不够就不管呗
var slice4 []int = []int{1, 2, 3, 4, 5}
var slice5 = make([]int, 10)
copy(slice5, slice4)
fmt.Println("slice4", slice4)
fmt.Println("slice5", slice5)
---
slice4 [1 2 3 4 5]
slice5 [1 2 3 4 5 0 0 0 0 0]
var a []int = []int{1, 2, 3, 4, 5}
var slice = make([]int, 1)
fmt.Println(slice)
copy(slice, a)
fmt.Println(slice)
---
[0]
[1]
函数中改变会影响到函数外部的改变:实参
var slice = []int{1, 2, 3, 4}
fmt.Println("slice", slice)
test(slice)
fmt.Println("test(slice)", slice)
}
func test(slice []int) {
slice[0] = 100
}
---
slice [1 2 3 4]
test(slice) [100 2 3 4]
string 与 slice#
string 底层是一个 byte 数组,因此 string 也可以进行切片处理
String 和切片在内存的形式,以 “abcd” 画内存示意图
string 是不可变的,不能通过 Str [0]=”Z” 方式来修改字符串
如果要修改字符串,可以先将 string 转 [] byte 或者将 [] rune-> 修改 -> 重写转成 string
str := "hello@atguige"
slice := str[6]
fmt.Println("slice", slice)
---
slice 97
---
str[0] = "z" //不可分配 string不可变
str := "hello@atguige" //atguige
slice := str[6:]
fmt.Println(slice) // atguige
str := "hello@atguige"
arr := []byte(str)
arr[0] = 'Z'
str = string(arr)
fmt.Println(str) //Zello@atguige
//英文和数字 []byte字节来处理,汉字是3个字节,因此会出现乱码
string转[]rune(切片,[]rune按字符处理)
str := "西京欢迎你"
arr := []rune(str)
arr[0] = '北'
str = string(arr)
fmt.Println(str) //北京欢迎你
练习#
说明:编写一个函数 fbn (n int) ,要求完成
1) 可以接收一个 n int
2) 能够将斐波那契的数列放到切片中
3) 提示,斐波那契的数列形式:
arr[0] = 1; arr[1] = 1; arr[2]=2; arr[3] = 3; arr[4]=5; arr[5]=8
思路:#
注意是 1,1,2,3,5,8
func fbn(n int)([]uint64){
fbnSlice := make([]uint64,n)
fbnSlice[0] = 1
fbnSlice[1] = 1
for i := 2; i < n; i++ {
fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2]
}
return fbnSlice
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: