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] = 1for i := 2; i < n; i++ {
fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2]}return fbnSlice
}