文章目录
一、数组Array——复合类型
1、介绍
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串、或者自定义类型。数组元素可以通过索引(位置)来读取(或者修改),索引从0开始,第一个元素索引为0,第二个索引为1,以此类推。数组的下标取值范围从0开始,到长度减1,数组大小一旦定义后,就不能改变。
2、数组的语法
声明和初始化数组
需要指名数组的大小和存储的数据类型
初始化数组中{}的元素个数不能大于[]中的个数
如果忽略[]中的数字不设置数组的大小,Go语言会根据元素的个数来设置数组的大小
未赋值时整型默认为0
var variable_name [size] variable_type
例子:
var balance [10] int32
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
其他创建方式
var a[4] int32 同 var a =[4] int
var a=[4] int {1,2,3,4}
var a=[5]int{1:1,2:2}//指定位置赋值,其余位置为0,[0 1 2 0 0]
d :=[...]int{1,2,3} //根据元素的个数,设置数组的大小
c :=[5] int {1:2}//指定位置赋值,其余为0
例子:
package main
import "fmt"
func main() {
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
balance1[1]=11
var a=[5]int{1:1,2:2}
fmt.Println(balance1[1])
fmt.Println(len(balance1))//容器中实际存储的数据量
fmt.Println(cap(balance1))//容器中能够存储的最大数量
fmt.Println(a)
//打印数组地址
fmt.Printf("%p\n",&a[1])//打印某个元素地址
fmt.Printf("%p",&a)//打印数组的初始地址
}
3、数组遍历for_range
//使用for循环遍历数组
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
for i:=0;i<len(balance1);i++{
fmt.Printf("%d ",balance1[i])
}
//使用range遍历数组,同时适用于map slice array string,需要接收两个数值,下标和对应下标上的数值
//1、下标和数值同时需要获取
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
for index,value:= range balance1{
fmt.Printf("%d %d",index,value)
}
//只需要值不需要下标
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
for _,value:= range balance1{
fmt.Printf("%d ",value)
}
4、打印数组的数据类型
//数组和数值都是值传递
package main
import "fmt"
func main() {
var balance1 = [10] int32{1,2,3,4,5,6,7,8,9,10}
fmt.Printf("%T\n",balance1)
//数组之间的比较,需要两个数组的大小和数据类型一样
var a =[10]int32{1,2}
fmt.Println(balance1==a)
}
输出结果:
[10]int32
false
5、值类型和引用类型
值类型:理解为存储的数值本身,将数据传递给其他的变量,传递的是数据副本(备份),int、float、string、bool、array
引用类型:理解为存储的数据的内存地址,slice,map…
6、数组的冒泡排序
//小到大排序
package main
import "fmt"
func main() {
var balance1 = [10] int32{1,234,67,89,5,89,0,8,91,10}
for i:=1;i<len(balance1);i++{
for j:=0;j<len(balance1)-i;j++{
if(balance1[j]>balance1[j+1]){
balance1[j],balance1[j+1]=balance1[j+1],balance1[j]
}
}
}
fmt.Println(balance1)
}
//大到小排序
package main
import "fmt"
func main() {
var balance1 = [10] int32{1,234,67,89,5,89,0,8,91,10}
for i:=1;i<len(balance1);i++{
for j:=0;j<len(balance1)-i;j++{
if(balance1[j]<balance1[j+1]){
balance1[j],balance1[j+1]=balance1[j+1],balance1[j]
}
}
}
fmt.Println(balance1)
}
7、多维数组
//存储的是一维的一维
//创建二维数组
例子:
var balance [10][1] int32
var balance1 = [10][1] int32{{1},{2}}
其他创建方式
var a[4][1] int32 同 var a =[4][1] int
var b=[4][1] int {{1}}
d :=[...][2]int{{1},{2}}
例子:
package main
import "fmt"
func main() {
var balance [10][1] int32
var balance1 = [10][1] int32{{1},{2}}
var a[4][1] int32
var b=[4][1] int {{1}}
d :=[...][2]int{{1},{2}}
fmt.Println(balance,"\n",balance1,"\n",a,"\n",b,"\n",d)
}
//遍历二维数组
package main
import "fmt"
func main() {
var balance [5][2] int
//存值
for i:=0;i<len(balance);i++{
for j:=0;j<len(balance[0]);j++{
balance[i][j]=j+1
}
}
//取值
for _,arr := range balance{
for _,value := range arr{
fmt.Print(value," ")
}
}
}
二、切片(Slice)
1、介绍
切片是对数组的抽象,与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大,切片是一种方便、灵活且强大的包装器。切片本身没有任何数据。它只是对现有数组的引用,切片与数组相比,不需要设置长度,在[]中不用设定值,相对来说比较自由
从概念上面来说切片像一个结构体,这个结构体包含了三个元素:
- 指针,指向数组中slice制定的开始位置
- 长度,即slice的长度
- 最大长度,也就是slice开始位置到数组的最后位置的长度
2、切片的语法
//定义切片,切片不需要说明长度,或使用make()来创建切片
//每个切片引用了一个底层数组
var 变量名 []数据类型
变量名 :=[] 数据类型
//第一个参数是类型slice、map、channel
//第二个参数:长度len
//第三个参数:容量cap,最多可以存储的元素数量
变量名 := make([]数据类型,len)
s1 := make([]int32,3,8)
例子
package main
import "fmt"
func main() {
s1 := make([]int,3,8)
fmt.Println(s1)
s2 :=[] int {1,2,3,4,5}
//切片末尾追加元素,就算超过最大容量也没事,会自动扩充(成倍增长,变为之前的两倍)
//切片一旦扩容,就是重新指向一个新的底层数组
for i:=1;i<=10;i++{
s1=append(s1, i)
fmt.Println(s1[i])
}
//将切片加入切片内
s3 :=append(s2, s1...)
fmt.Println(s3)
///遍历切片
for i:=0;i<11;i++{
fmt.Print(s3[i]," ")
}
for _ ,slice :=range s3{
fmt.Println(slice )
}
//切片扩容,切片扩容是重新指向一个新的底层数组
fmt.Printf("%d %d\n",len(s3),cap(s3))//18 18
s3=append(s3,1,2,3)
fmt.Printf("%d %d\n",len(s3),cap(s3))//18 36
//打印切片底层地址
fmt.Printf("%p",s1)//打印底层地址,注意这里没有+&
}
3、在已有的数组上创建切片
package main
import "fmt"
func main() {
/*
在已有的数组上,直接创建切片,该切片的底层数组 就是当前的数组
语法:
slice :=arr[start:end]
切片的中的数据:[start:end)
arr[:end]从头到end
arr[start:]从start到尾
长度是start到end,容量是切片start到末尾
*/
a:=[10]int{1,2,3,4,5,6,7,8,9,10}
s1:=a[:5]//取前五个数字 start0 末尾10 cap10
s2:=a[5:]//取剩下的数字 start5 末尾10 cap5
s3:=a[3:8]//取第四位到第七位 start3 末尾10 cap7
s4:=a[:]//取全部 start0 末尾10 cap10
fmt.Printf("s1地址:%p %d %d ",s1,len(s1),cap(s1))
fmt.Println(s1)
fmt.Printf("s2地址:%p %d %d ",s2,len(s2),cap(s2))
fmt.Println(s2)
fmt.Printf("s3地址:%p %d %d ",s3,len(s3),cap(s3))
fmt.Println(s3)
fmt.Printf("s4地址:%p %d %d ",s4,len(s4),cap(s4))
fmt.Println(s4)
//更改数组内容,切片如果包含此元素也会被修改
fmt.Printf("------------------------------------------------------\n")
a[5]=100
fmt.Println(a) //{1,2,3,4,5,100,7,8,9,10}
fmt.Println(s1)//[1 2 3 4 5]
fmt.Println(s2)//[100 7 8 9 10]
fmt.Println(s3)//[4 5 100 7 8]
fmt.Println(s4)//[1 2 3 4 5 100 7 8 9 10]
//更改数组内容,对数组切片进行增加数据,因为是同一个底层数组,
//所以如果另一个切片和更改元素的切片与原数组对应的位置,则另一个数组也会同样被改变
//当该切片扩容之后,就与原来的其他同一数组的切片无关系
fmt.Printf("------------------------------------------------------\n")
//[1 2 3 4 5] 5 10
s1=append(s1,6,7,8)
s1=append(s1,6,7,8)
fmt.Println(a)//[1 2 3 4 5 6 7 8 9 10]
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
s1[1]=100;
fmt.Println(s1)
fmt.Println(s4)
}
4、值传递与地址传递
值传递:int、float、string、bool、array、传递的是数据副本
引用类型 Slice,传递的地址,多个变量指向了同一块内存地址,所以当该切片未进行扩容时都是属于同一个内存地址
5、深拷贝与浅拷贝
/*深拷贝:拷贝的是数据本身。
值类型的数据,默认都是深拷贝:array,int,float,string,bool,struct
浅拷贝:拷贝的是数据地址。
导致多个变量指向同一块内存
引用类型的数据,默认都是浅拷贝:slice,map
func copy(接受拷贝的切片,被拷贝的切片)可以实现切片的深拷贝
*/
package main
import "fmt"
func main() {
s1:=[] int{1,2,3,4}
s2:=make([]int,0)//最大容量为0
//将切片s1中的元素加入切片是s2
for i:=0;i<len(s1);i++{
s2=append(s2,s1[i])
}
fmt.Println(s1)// [1 2 3 4]
fmt.Println(s2)//[1 2 3 4]
/*
此时改变切片s1元素不会造成切片s2对应元素改变,因为当切片进行扩容时就会重新指向一个新的底层地址
所以在给切片s2赋值的时候,切片s2进行了扩容,使得切片s2所指向的底层地址和切片s1指向的底层地址不是同一个,
故改变切片s1的值的时候,切片s2的值不变
*/
s1[0]=100
fmt.Println(s1)//[100 2 3 4]
fmt.Println(s2)//[1 2 3 4]
//使用copy()对切片进行深拷贝
/*
copy(拷贝后切片a,被拷贝的切片b)
拷贝后的切片大小和拷贝的内容的多少取决于a原本的大小,如果a(大小1)的大小小于b(大小2),a=b[:len(a)]
如果a(大小3)的大小大于b(大小2),a接受b的全部元素,a的组成=b的全部元素+a未被b覆盖的元素,
就是a=b+a[len(b):]
*/
s3:=[] int {7,8,9,10,11}//s2=[1 2 3 4]
s4:=[]int {10}
s5:=[]int {1,2}
s6:=[]int {9,2}
copy(s3,s2)//将s2的元素拷贝到s3对应的位置中
copy(s4,s2)//将s2拷贝到s4中
copy(s5,s2[2:])//将s3后三个元素(3,4,11)拷贝给s5
copy(s6[1:],s2)//将s2拷贝给s6的第二个位置开始
fmt.Println(s3)//[1 2 3 4 11]
fmt.Println(s4)//[1]
fmt.Println(s5)//[3 4]
fmt.Println(s6)//[9 1]
}