本章纲要
一、数组
1、数组的定义
var 数组名 [数组大小] 数组类型
2、数组内存
(1)数组的地址可以通过数组名来获取 &intArr
(2)数组的第一个元素的地址,就是数组的首地址
3、数组初始化
package main
import (
"fmt"
)
func main() {
var numArr01 [3]int = [3]int{1,2,3}
fmt.Println("numArr01=",numArr01)
var numArr02 = [3]int {5,6,7}
fmt.Println("numArr01=",numArr02)
var numArr03 = [...]int{8,9,10}
fmt.Println("numArr01=",numArr03)
var numArr04 = [...]int{1:800,0:900,2:999}
fmt.Println("numArr01=",numArr04)
}
4、数组遍历
(1)常规遍历 for循环
(2)for-range 遍历访问数组元素
1)语法
for index, value := range arry {
} //arry是数组
2)说明
1)index————>下标
2)value————>数组下标对应的值
3)index,value是块间变量,出了for块就不能用了
4)index,value不是固定名称,可以自己命名
5)不想要index可以用_占位符代替
5、数组的细节和说明
(1)、数组是相同数据类型的组合,一旦长度定下来,就不能动态改变
(2)、var arr []int 此时arr就是一个slice切片 //没有写大小
(3)、数组中的元素可以是任意类型,包括值类型和引用类型,但是不可以混用
(4)、数组创建后如果没有赋值则是默认值
(5)、下标从0开始
(6)、数组是值类型,数组间不会相互影响
(7)、如果想在其他函数中修改原来的数组,就要使用引用方式(指针方式)
(8)、在传递数组时要传递数组的长度
6、举例
(1)、随机生成5个数,并将其翻转打印
此处需要说明生成随机数函数
package main
import (
"fmt"
_ "fmt"
"math/rand"
)
func main(){
var intArr [5]int
for i:=0;i<len(intArr);i++{
intArr[i] = rand.Intn(100) //0<n<100
}
fmt.Println(intArr)
}
但是此函数若是Seed不变,那么生成的随机数都不会变了,如一直是生成这个
所以此处要加上我们上一章所学的时间戳来作为Seed种子
package main
import (
"fmt"
_ "fmt"
"math/rand"
"time"
)
func main(){
var intArr [5]int
rand.Seed(time.Now().UnixNano()) //***********
for i:=0;i<len(intArr);i++{
intArr[i] = rand.Intn(100) //0<n<100
}
fmt.Println(intArr)
}
此处选择纳秒比较妥当,因为秒相同则Seed还是相同的,所以纳秒相同的几率更小
下面是进行交换后的完整代码
package main
import (
"fmt"
_ "fmt"
"math/rand"
"time"
)
func main(){
var intArr [5]int
rand.Seed(time.Now().UnixNano())
for i:=0;i<len(intArr);i++{
intArr[i] = rand.Intn(100) //0<n<100
}
fmt.Println(intArr)
//交换
temp :=0
for i:=0;i<len(intArr)/2;i++{
temp = intArr[len(intArr)-1-i]
intArr[len(intArr)-1-i] = intArr[i]
intArr[i] = temp
}
fmt.Println(intArr)
}
运行结果
二、切片
1、引言
需要一个数组保存学生的成绩,但是个数不确定,则使用切片(可以暂时理解为动态数组)
2、基本概念
(1)slice
(2)切片是数组的一个引用,在传递时要遵守引用机制
(3)切片的使用和数组类似,遍历切片、访问切片的元素和求切片的长度len(slice)都一样
(4)切片的长度可以变化,因此切片是一个可以动态变化的数组
(5)基本语法
var 变量名[]类型
比如 var a []int
3、举例说明
package main
import (
"fmt"
)
func main() {
//演示切片的使用
var intArr [5]int = [...]int{1,2,3,4,5}
//slice := intArr[1..3]
//1.slice 就是切片名
//2.intArr[1:3]表示slice引用到intArr这个数组
//3.引用intArr数组里的起始下标为1,最后的下标为3(但是不包括3)
slice := intArr[1:3]
fmt.Println("intArr:",intArr)
fmt.Println("slice:",slice)
fmt.Println("slice个数:",len(slice))
fmt.Println("slice容量:",cap(slice))
}
4、切片内存
对上图总结:
5、切片使用的三种方式
(1)定义一个切片,然后让切片去引用一个已经创建好的数组,例如上面的案例
(2)通过make来创建切片
1)基本语法:
var 切片名 []type = make([],len,[cap])
cap 可以不写,若写,那么cap>len
默认情况下,创建好的切片内元素都为0
访问切片则如
slice[0]
2)举例
package main
import (
"fmt"
_ "fmt"
)
func main() {
//演示切片的使用 make
//切片要make后使用
var slice []float64 =make([]float64,5,10)
fmt.Println(slice)
slice[1]=10
slice[3]=20
fmt.Println(slice)
fmt.Println("size=",len(slice))
fmt.Println("cap=",cap(slice))
}
此时就只能通过slice操作元素,而没有数组了
3)小结:
①通过make创建slice,可以指定切片的大小和容量
②如果没有给切片的各个元素赋值,那么就会使用默认值[int float]——>0;string——>"";bool——>false
③通过make 创建的切片对应的数组是由make底层维护,对外不可见,即只能通过slice操作元素
(3)定义一个切片,直接指定具体数组,原理类似make
//第三种方式
var slice3 []string = []string{"tom","jack","mary"}
fmt.Println(slice3,len(slice3),cap(slice3))
结果:
(4)使用方式总结:
1)方式1和方式2的区别:
①方式1是直接引用数组,这个数组是直接存在的,程序员是可见的
②方式2是通过make来创建切片,make也会创建一个数组,是由切片在底层进行维护,程序员是看不见的,make创建切片示意图:
6、切片的遍历
(1)for循环
(2)for-range遍历
package main
import (
"fmt"
_ "fmt"
)
func main() {
//使用常规for
var arr [5]int = [...]int{10,20,30,40,50}
slice :=arr[1:4]
for i:=0;i<len(slice);i++ {
fmt.Printf("slice[%v]=%v ", i, slice[i])
}
fmt.Print("\n")
//for-range
for j,v :=range slice{
fmt.Printf("slice[%v]=%v ", j, v)
}
}
两种方式结果
7、append()内置函数
(1)append内置函数(),可以对切片进行动态增长
//append()
var slice2 []int =[]int{100,200,300}
//生成新切片
slice3 := append(slice2, 400)
fmt.Println("slice3=",slice3)
//改变原来切片
slice2 = append(slice2,400)
fmt.Println("slice2=",slice2)
//切片再追加切片(也可以是其他切片,但是不能是数组)
slice4 := append(slice2,slice2...)
fmt.Println("slice4=",slice4)
(2)对上面代码分析
1)append本质是扩容
2)go底层会创建一个新的数组newArr(安装扩容后大小)
3)将slice原来包含的元素拷贝到新的数组newArr
4)slice重新引用到newArr
5)注意newArr是在底层来维护的,程序员不可见
8、copy()函数
切片使用copy函数完成拷贝操作,两切片相互独立不影响。
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
9、切片的细节
(1)切片初始化时,仍然不可以越界,范围在【0,len(arr)-1】之间,但是可以动态增长
(2)简写
1)var slice = arr[0:end] ——》 var slice = arr[:end]
2)var slice = arr[start:len(arr)] ——》 var slice = arr[start:]
3)var slice =arr[0:len(arr)] ——》 var slice = arr[:]
(3)切片定义完后不能直接使用,要引到已经存在的数组或者make后使用
(4)切片可以继续切片
10、string和slice
(1)string是一个byte数组,所以string 可以进行切片操作
(2)string是不可变的,也就是说不可以通过str[0]来修改字符串
(3)如果修改字符串,可以string——>[]byte或者[]rune——>修改——>重写转成string
//string和slice
str := "hello world"
slicestr :=str[6:]
fmt.Print(slicestr)
//修改字符串 将"hello world"改成"zello world"
arr1 :=[]byte(str) //不能处理中文,因为byte是字节,若是中文就会乱码(一个汉字是3个字节)
arr1[0] = 'z'
str = string(arr1)
fmt.Println("\nstr=",str)
//修改字符串 将"hello world"改成"世界llo world"
arr2 :=[]rune(str) //处理中文
arr2[0] = '世'
arr2[1] = '界'
str = string(arr2)
fmt.Println("\nstr=",str)
三、二维数组
1、语法
var 数组名 [大小][大小]类型
例如 var arr [2][3]int