概念:存储一组相同数据类型的容器
/*array 定义*/
var variable_name [SIZE] variable_type
/*定义*/
var array0 [5]int
var array1 = [5]int{54, 5}
var array2 = [5]int{2: 54, 4: 5}//将数组的第2位设置为54,第4位设置为5
var array3 = [...]int{54, 7: 5}//可不设置array的长度,会根据后续定义的数组自动确定数据长度
fmt.Println("数组长度:", len(array0))
fmt.Println("数组容量:", cap(array0))
fmt.Println(array2) //output:[0,0,54,0,5]
fmt.Println(len(array3))//output:8
len(array)表示获取数组实际存储的数据长度
cap(array)表示数组本身可存储的数据容量
数组内存分析:在计算机内存空间中开辟连续内存空间,数组地址是数组中的首地址,也就是数组中第一个元素的地址。
数组访问:
可使用range遍历数组
for i, v := range array3 {
fmt.Printf("下标:%d 数值:%d\n", i, v)
}
//i表示读取到数组下标,v表示读取到的数组数据
数组类型:值类型
所谓的值类型指的是数组在传递过程中,传递的是数据副本。
例子:
var array00 = [5]int{54, 5}
array01 := array00
fmt.Println(array00) //[54 5 0 0 0]
fmt.Println(array01) //[54 5 0 0 0]
array01[0] = 100
fmt.Println(array00) ////[54 5 0 0 0]
fmt.Println(array01) //[100 5 0 0 0]
将array00赋值给array01,然后修改array01中第0位的数值,此时array00不变,array01的第0位数值发生改变,这就是值传递。在赋值过程中,实际上array00将自身的数据复制了一份,然后将复制的这份副本数据传递给array01,所以当array01数组改变自身数据时,并不影响array00的数据。
数组排序:
/*冒泡排序*/
var array_sort = [5]int{19, 23, 8, 10, 6}
for i := 1; i < len(array_sort); i++ {
for j := 0; j < len(array_sort)-i; j++ {
if array_sort[j] > array_sort[j+1] {
array_sort[j], array_sort[j+1] = array_sort[j+1], array_sort[j]
}
}
fmt.Println(array_sort)
}
fmt.Println(array_sort)
概念:由于数组是定长的,定义数组时定义了数组的长度后便无法改变数组大小,而slice则弥补了这一缺陷,可以把slice理解为一种动态数组或变长数组。切片实际上是对数组的引用,切片本身是没有数据的,切片的数据来源于其指向的底层数组中的数据。
/*定义*/
var slice0 []int
fmt.Println(slice0) //[]
var slice1 = []int{45, 5, 6}
fmt.Println(slice1) //[45 5 6]
fmt.Printf("%T\n", slice1) //[]int
其定义方式与数组定义方式类似,只是无需确定size的大小
一般的,go中可通过make函数来创建切片
make函数是专门用来创建引用类型的数据,比如slice、map、chan
/*
make函数:func make(t Type, size ...IntegerType) Type
第一个参数表示要创建的数据类型;
第二个参数表示当前数据类型的长度;
第三个参数表示其容量的大小
*/
slice2 := make([]int, 3, 7)
fmt.Println(slice2) //[0 0 0]
/*
append函数:专门用来对slice操作,向slice末端添加数值
slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)
*/
slice2 = append(slice2, 2, 7, 8, 9, 6, 3)
fmt.Println(slice2) //[0 0 0 2 7 8 9 6 3]
slice2 = append(slice2, slice1...)//向slice2中添加另一个slice,必须加...
fmt.Println(slice2) //[0 0 0 2 7 8 9 6 3 45 5 6]
切片内存分析:上面提到过,切片是引用类型的数据,其本身并不存放数值,切片存放的实际上是指向底层数组的地址。
切片在扩容时是在之前容量的基础上,成倍增长;
扩容之后,切片便会指向一个新的底层数组的地址
slice3 := make([]int, 0, 2)
fmt.Println(slice3) //[]
fmt.Printf("len:%d,cap:%d\n", len(slice3), cap(slice3)) //len:0,cap:2
fmt.Printf("%p\n", slice3) //0xc0000bc200
slice3 = append(slice3, 1, 2, 3)
fmt.Println(slice3) //[1 2 3]
fmt.Printf("len:%d,cap:%d\n", len(slice3), cap(slice3)) //len:3,cap:4
fmt.Printf("%p\n", slice3) //0xc0000be040
还可通过在现有数组的基础上进行操作得到切片:在现有的数组上创建切片,在修改数组/切片的内容时,对应的切片/数组的内容也会随之改变;同时需要注意基于现有数组创建的切片的长度和容量大小;在对切片添加数据时,倘若未超出切片当前容量,那么该切片所对应的数组地址与原始数组地址是相同的,一旦需要扩容,则该切片所指向的数组地址与原始数组地址不同。
a := [5]int{1, 2, 3, 4, 5}
fmt.Println("------------已有数组创建切片----------------")
s1 := a[:4]
s2 := a[2:5] //左闭右开
s3 := a[1:3]
fmt.Print("a, s1, s2, s3:")
fmt.Println(a, s1, s2, s3)
fmt.Printf("a--->%p\n", &a)
fmt.Printf("s1--->%p\n", s1)//这里注意不能加&,因为切片s1是引用类型数据,s1表示的就是所指向的底层数组的地址,如果写作&s1,那么获取到的则是切片的地址
/*数组a的长度和容量都为5,对于s1来说,相当于取了a所有的数据作为一个切片,s1当前的长度和容量都是5
s2是从a[2]开始取数据,取到a[4],其长度为2,其容量是len(a)-2=3
s3是从a[1]开始取数据,取到a[2],其长度为2,其容量是len(a)-1=4*/
fmt.Printf("s1 -->len:%d, cap:%d\n", len(s1), cap(s1)) // s1 -->len:5, cap:5
fmt.Printf("s2 -->len:%d, cap:%d\n", len(s2), cap(s2)) // s2 -->len:3, cap:3
fmt.Printf("s3 -->len:%d, cap:%d\n", len(s3), cap(s3)) // s3 -->len:2, cap:4
fmt.Println("------------更改数组内容----------------")
a[3] = 444
fmt.Print("a, s1, s2, s3:")
fmt.Println(a, s1, s2, s3)
fmt.Println("------------更改切片内容----------------")
s2[0] = 300 //s2[0]实际上是a[2]
fmt.Print("a, s1, s2, s3:")
fmt.Println(a, s1, s2, s3)
fmt.Println("------------切片添加内容----------------")
s1 = append(s1, 777)
fmt.Print("a, s1, s2, s3:")
fmt.Println(a, s1, s2, s3)
fmt.Println("------------切片添加内容并扩容----------------")
s1 = append(s1, 9, 9, 9, 9)
fmt.Print("a, s1, s2, s3:")
fmt.Println(a, s1, s2, s3)
fmt.Printf("a--->%p\n", &a)
fmt.Printf("s1--->%p\n", s1)
/*output
------------已有数组创建切片----------------
a, s1, s2, s3:[1 2 3 4 5] [1 2 3 4] [3 4 5] [2 3]
a--->0xc000018480
s1--->0xc000018480
s1 -->len:4, cap:5
s2 -->len:3, cap:3
s3 -->len:2, cap:4
------------更改数组内容----------------
a, s1, s2, s3:[1 2 3 444 5] [1 2 3 444] [3 444 5] [2 3]
------------更改切片内容----------------
a, s1, s2, s3:[1 2 300 444 5] [1 2 300 444] [300 444 5] [2 300]
------------切片添加内容----------------
a, s1, s2, s3:[1 2 300 444 777] [1 2 300 444 777] [300 444 777] [2 300]
------------切片添加内容并扩容----------------
a, s1, s2, s3:[1 2 300 444 777] [1 2 300 444 777 9 9 9 9] [300 444 777] [2 300]
a--->0xc000018480
s1--->0xc00001e0f0
*/
切片类型:引用类型
在传递时是传递的引用地址,即指向底层数组的地址
概念:map实际上就是一些键值对(key-value),键对应着值
/*定义map*/
var map_variable map[key_data_type]value_data_type
var map0 map[int]string//方式一
var map1 = make(map[int]string)//方式二
map2 := map[string]float32{"C++":5.50,"GO":5.00}//方式三
map是引用类型,如果不初始化map就会产生一个nil map,相当于是一个空map。
对于方式一来说:其创建的是nil map,并未初始化,如果对其进行赋值的话会报错
而对于方式二来说,虽然没有赋值,但是已经将其初始化了,因此可进行赋值。因此在对map进行操作时可添加如下判断:
if map0 == nil {
map0 = make(map[int]string)
}
/*获取map值*/
map0[0] = "helloworld"
map0[1] = "hellochina"
v1, ok1 := map0[1]
v2, ok2 := map0[2]
fmt.Println(map0)//map[0:helloworld 1:hellochina]
fmt.Println(v1, ok1)//hellochina true
fmt.Println(v2, ok2)// false
/*获取map值时,如果索引号key之前并未赋值,
那么返回的value则是该value类型的默认值,
比如int-->0,float-->0.0,string-->""等等,
为了判断key值是否存在,在获取map值时:
可以采用value, ok := map[number]的操作,
其中value返回的是以number(key值)为索引的value值,
ok为true则表示当前key值存在,可获取到对应的value值,
如果为false则表示key值不存在,获取到的是默认值*/
/*修改map的value值*/
map0[1]="CHINA"//直接利用key值进行操作即可
/*删除map的value值*/
delete(map0,1)//直接对key值进行操作即可
/*遍历map*/
for key, value := range map0 {
fmt.Printf("key:%d---->value:%s\n", key, value)
}
map1[1] = "样"
map1[11] = "我"
map1[13] = "AI"
map1[2] = "人工"
map1[6] = "自动化"
map_slice := make([]int, 0)
for key, value := range map1 {
fmt.Printf("key:%d---->value:%s\n", key, value)
map_slice = append(map_slice, key)
}
fmt.Println(map_slice)
/*冒泡排序*/
/*
for i := 1; i < len(map_slice); i++ {
for j := 0; j < len(map_slice)-i; j++ {
if map_slice[j] > map_slice[j+1] {
map_slice[j], map_slice[j+1] = map_slice[j+1], map_slice[j]
}
}
}
*/
/*sort包中的Ints函数对Int数据类型排序,默认是升序*/
sort.Ints(map_slice)
fmt.Println(map_slice)
for i := 0; i < len(map_slice); i++ {
fmt.Printf("key:%d---->value:%s\n", map_slice[i], map1[map_slice[i]])
}
for i := len(map_slice) - 1; i >= 0; i-- {
fmt.Printf("key:%d---->value:%s\n", map_slice[i], map1[map_slice[i]])
}
map类型:引用型,传递的是引用地址