go 语言中的数组array
1.数组的声明:需要指明数组的大小和存储的数据类型
-
var 数组名 [数组长度] 数组的数据类型 -- 如:var num [128] int
-
var 数组名 = [数组长度] 数组的数据类型 -- 如:var num = [128] int
可以在声明时赋初值:var num = [4] int{1,2,3,4},不赋初值的默认为0
其他:
var num [4]intnum[0] = 100num[1] = 200num[2] = 300fmt.Println("length:", len(num))fmt.Println("lebngth", cap(num))
2.数组的遍历
使用for循环遍历数组
var num [4]int
num[0] = 100
num[1] = 200
num[2] = 300
num[3] = 400
for i := 0; i < len(num); i++ {
fmt.Println("num = ", num[i])
}
使用for..range来遍历数组
range:指的是范围,不需要操作数组的下标,到达数组的末尾,自动结束for range循环,没出都从数组中获取下标和对应的数值
格式:
for 存放下标的变量,存放数值的变量 := range 数组名{
///
}
如:for index,value := range num {
fmt.Printf("下标是%d,数值是%d\n",index,value)
}
若是你只需要值而希望忽略索引,则可以使用_blank标识符来替换索引来实现这一点
如:
for _,value := range num {
sum += value
}
fmt.Println(sum)
4.数组的排序
冒泡排序:两个循环嵌套,外层执行(长度-1)次,内层执行(长度-i)次,循环体比较相邻两数大就交换。
5.多维数组
跟C语言差不多
定义格式:
var name = [3] [4] int{{1,2,3,4},{5,6,7,8},{9,10,11,12}}
name := [3] [4] int {{1,2,3,4},{5,6,7,8},{9,10,11,12}}
6.其他事项
数组是一个值传递的数据类型,也叫作值类型,值类型传递数据时传递的是数据的副本。
num1 := [2]int{1,2}
num2 := num1
num2[0] = 2 //更改num2中的数据不会影响到num1
每一个数组都有自己的数据类型,当你用%T去判断数组的数据类型时得到的是[size]type这种形式的类型
num := [2]int{2,2}
fmt.Printf("%T",num)//结果是[2]int
切片
什么是切片slice?
首先说一下数组,数组是用来存储一组相同数据类型的数据结构,是定长的
切片与数组类似,不同的是切片是变长的,它的长度是可以改变的,所以也叫作变长数组或者动态数组
切片是一种方便、灵活且强大的包装器,切片本身没有任何数据,他们只是对现有数组的引用。
切片在使用时不需要在[]中指定长度值
从概念上来讲slice像一个结构体,这个结构体中包含了三个元素:
-
指针,指向数组中slice指定的开始位置
-
长度,即slice的长度
-
最大长度,也就是slice开始位置到数组的最后位置的长度
切片的语法
定义切片:
var 切片名 []数据类型
简写:切片名 := []数据类型{数据值}
一般喜欢使用go语言自带的make()函数来创建切片
make(type,len,cap)
type:类型,可选slice、map、chan,创建切片的话可以写 ==> []数据类型
len:长度,实际存储元素的数量
cap:容量,最多能够存储的元素的数量
例:
a := make([]int,3,8)
fmt.Println(a)//输出结果[0 0 0]
从已有数组中创建切片
语法:切片名 := 数组名[开始下标:结束下标]
语法中开始下标和结束下标都可以省略不写,此时分别表示的是从数组头开始和直到数组结尾。
注意,切片取得范围是左闭右开的,如:a[3:8]表示的是从a[3]到a[7]
从已有数组中创建切片,切片的容量是怎么算的呢?
举个例子来说明,若已有一个数组a为{1,2,3,4,5,6,7,8,9,10}
创建的切片为s1 := a[0:5],则该切片指向的地址是a[0]的地址,即数组的首地址,其长度为5,容量为10,容量为10的原因是指向数组的容量为10
创建的切片为s2 := a[3:8],则该切片指向的地址是a[3]的地址,其长度为5,容量为7,容量为7的原因是从a[3]到数组结尾剩余的容量为7
切片的操作
操作切片中数据的方式和数组一样,都是切片名和下标来访问切片中的元素,如slice[0],slice[2]8
向切片的尾部追加元素 append()
slice = append(slice,elem1,elem2)
slice = append(slice,anotherSlice...)
示例:
func main() { fmt.Println("HelloWorld!") slice1 := []int{1, 2, 3} fmt.Println(slice1) slice2 := make([]int, 0, 8) fmt.Println(slice2) slice2 = append(slice2, 1, 2, 3, 4) fmt.Println(slice2) slice2 = append(slice2, slice1...) fmt.Println(slice2) }
结果:
HelloWorld! [1 2 3] [] [1 2 3 4] [1 2 3 4 1 2 3]
遍历切片和遍历数组的方式一样,不在赘述,贴一个例子
for i := 0; i < len(slice2); i++ { fmt.Printf("%d\t", slice2[i]) } fmt.Println() for _, val := range slice2 { fmt.Printf("%d\t", val) }
结果:
1 2 3 4 1 2 3 1 2 3 4 1 2 3
切片的内存分析
切片是一个引用类型的数据类型
1.每一个切片引用了一个底层数组
2.切片本身不存储任何数据,都是这个底层数组存储,随意修改切片也就是修改这个数组中的数据
3.当向切片中添加数据时,如果没有超过容量,直接添加,如果超过了容量则自动扩容(成倍增长:2==》4==》8==》16)(成倍增长!如容量为2,要插入共51个元素,扩容后容量为52)
4.切片一旦扩容,就是重新指向一个新的底层数组
演示实例:
func main() { fmt.Println("HelloWorld!") slice1 := make([]int, 0, 2) slice1 = append(slice1, 1, 2) fmt.Println(slice1) fmt.Printf("len = %d,cap = %d\n", len(slice1), cap(slice1)) slice1 = append(slice1, 3, 4, 5, 6, 7) fmt.Println(slice1) fmt.Printf("len = %d,cap = %d\n", len(slice1), cap(slice1)) slice1 = append(slice1, 8, 9, 10) fmt.Println(slice1) fmt.Printf("len = %d,cap = %d\n", len(slice1), cap(slice1)) }
结果
HelloWorld! [1 2] len = 2,cap = 2 [1 2 3 4 5 6 7] len = 7,cap = 8 [1 2 3 4 5 6 7 8 9 10] len = 10,cap = 16
使用切片的注意点
由于切片是引用类型的数据,所以操作切片的数据时,实质就是在操作所指向底层数组的数据,因此底层数组的数据是会改变的(浅拷贝),若是同时有别的切片指向了这个底层数组,则该切片中的数据也会相应的改变。(此时指的是切片的容量足够的时候,即没有发生扩容)
当切片的容量不足够存放数据时,会拷贝底层数组的数据到一个新的数组(创建后将数据拷贝过去,容量是原数组的倍数)中,再将切片的指针指向新的数组。在这种情况下,是不会对原数组的数据产生影响的。因此若是有别的切片指向了原数组,切片中的数据也不会被改变。
深拷贝和浅拷贝
深拷贝:拷贝的是数据本身
值类型的数据默认都是深拷贝:array,int,float,string,bool,struct
浅拷贝:拷贝的是数据地址
会导致多个变量指向同一块内存
引用类型的数据默认都是浅拷贝:slice,map
go语言内置的函数:copy()将原切片中的元素拷贝到新切片中
func copy(dst,src []type)int
例一:
s1 := []int{1,2,3,4}
s2 := []int{5,6,7}
copy(s1,s2)//将s2的元素拷贝到s1中,注意,这不是追加到s1的末尾而是从第一个元素开始替换掉
fmt.Println(s1)//结果:[5,6,7,4]
fmt.Println(s2)//结果:[5,6,7]
例二:
s1 := []int{1,2,3,4}
s2 := []int{5,6,7}
copy(s2,s1[2:])//将s1中下标2到末尾的元素拷贝到s2中
fmt.Println(s1)//结果:[1,2,3,4]
fmt.Println(s2)//结果:[3,4,7]
例三:
s1 := []int{1,2,3,4}
s2 := []int{5,6,7}
copy(s2[1:],s1)//将s1中的元素拷贝到s2中下标1之后的位置,若是s2的大小不够的话是不会自动扩容的
fmt.Println(s1)//结果:[1,2,3,4]
fmt.Println(s2)//结果:[5,1,2]//s2的容量只有3,所以只拷贝了s1中的前两个元素