公司的Tony老师这两天请假,请了他的好朋友Kevin来代课。这两个人的风格真的很不一样。托尼缓慢而温柔。它有多轻?我洗头的时候,不知道他是不是怕弄湿我的头发。另一方面,凯文嗓音洪亮,精力充沛。说给我洗头,可我感觉他是在拍我的头。水花在我眼前飞溅,水雾缭绕,仿佛能看到彩虹。理发的小小感觉有点夸张。经过上一篇的学习,我对围棋的认识应该是越来越深了。今天,我们来聊聊更进阶的内容:复杂数据类型。本文主要介绍数组和切片。开征~array数组有两个特点:Fixed-length相同的元素类型正是因为它的长度是固定的,所以相对于slice来说,在开发过程中用的比较少。但是数组是切片的基础。了解数组后,学习切片就容易多了。声明和初始化声明了一个长度为3且元素类型为int的数组。数组元素按索引访问,索引从0到数组长度减1,内置函数len可以得到数组长度。vara[3]int//输出数组的第一个元素fmt.Println(a[0])//0//输出数组的长度fmt.Println(len(a))//3的初始值数组是元素类型的零值,也可以用数组字面量初始化数组。//数组字面量初始化varb[3]int=[3]int{1,2,3}varc[3]int=[3]int{1,2}fmt.Println(b)//[123]fmt.Println(c[2])//0如果指定的数组长度没有显示,但是...,则数组长度由实际元素个数决定。//使用...d:=[...]int{1,2,3,4,5}fmt.Printf("%T\n",d)//[5]int也可以指定索引position来初始化,如果不指定数组长度,则长度由索引决定。//指定索引位置初始化e:=[4]int{5,2:10}f:=[...]int{2,4:6}fmt.Println(e)//[50100]fmt.Println(f)//[20006]多维数组多维数组的声明和初始化是一样的。这里我们以二维数组为例。需要注意的一点是,只允许使用多维数组的第一维....//二维数组varg[4][2]inth:=[4][2]int{{10,11},{20,21},{30,31},{40,41}}//声明并初始化外层数组中索引为1和3的元素i:=[4][2]int{1:{20,21},3:{40,41}}//声明并初始化外层数组和内部数组的单个元素j:=[...][2]int{1:{0:20},3:{1:41}}fmt.Println(g,h,i,j)使用数组只要数组元素是可比较的,并且数组的长度是数组类型的一部分,它们就是可比较的。所以[3]int和[4]int是两种不同的类型。//数组比较a1:=[2]int{1,2}a2:=[...]int{1,2}a3:=[2]int{1,3}//a4:=[3]int{1,2}fmt.Println(a1==a2,a1==a3,a2==a3)//真假//fmt.Println(a1==a4)//无效操作:a1==a4(mismatchedtypes[2]intand[3]int)Arraytraversal://数组遍历fori,n:=rangee{fmt.Println(i,n)}值类型Go数组是值类型,赋值和传参都会复制整个数组。从输出可以看出,内容是一样的,只是地址不同。packagemainimport"fmt"funcmain(){//数组拷贝x:=[2]int{10,20}y:=xfmt.Printf("x:%p,%v\n",&x,x)//x:0xc00012e020,[1020]fmt.Printf("y:%p,%v\n",&y,y)//y:0xc00012e030,[1020]test(x)}functest(a[2]int){fmt.Printf("a:%p,%v\n",&a,a)//a:0xc00012e060,[1020]}再看函数参数传递:packagemainimport"fmt"funcmain(){x:=[2]int{10,20}//参数修改(x)fmt.Println("main:",x)//main:[1020]}funcmodify(a[2]int){a[0]=30fmt.Println("modify:",a)//modify:[3020]}从结果中也可以看出,修改modify中的数组内容后,main中的数组内容没有变化。那么,是否可以在函数内修改,在函数外影响呢?答案是肯定的,接下来要说的切片就可以做到。slicesliceslice是一个引用类型,它具有三个属性:指针、长度和容量。指针:指向切片可访问的第一个元素。长度:切片中元素的数量。容量:切片的起始元素和底层数组的最后一个元素之间的元素数。看到这样的解释是不是一头雾水?别着急,让我们详细解释一下。它的底层结构如下:让我们再看一个例子,看看每个部分是什么意思。最底层是一个包含10个整数元素的数组,data1指向数组的第四个元素,长度为3,容量取到数组的最后一个元素,即7。data2指向数组的第五个元素数组,长度为4,容量为6。创建切片有两种方式:第一种方式是基于数组创建切片://基于数组创建切片vararray=[...]int{1,2,3,4,5,6,7,8}s1:=array[3:6]s2:=array[:5]s3:=array[4:]s4:=array[:]fmt.Printf("s1:%v\n",s1)//s1:[456]fmt.Printf("s2:%v\n",s2)//s2:[12345]fmt.Printf("s3:%v\n",s3)//s3:[5678]fmt.Printf("s4:%v\n",s4)//s4:[12345678]第二种方式是使用内置函数make创建://usemaketocreateslices//len:10,cap:10a:=make([]int,10)//len:10,cap:15b:=make([]int,10,15)fmt.Printf("a:%v,len:%d,cap:%d\n",a,len(a),cap(a))fmt.Printf("b:%v,len:%d,cap:%d\n",b,len(b),cap(b))使用切片遍历与遍历数组相同。//切片遍历fori,n:=ranges1{fmt.Println(i,n)}比较不能用==来测试两个切片是否有相同的元素,但是切片可以用nil进行比较。slice类型的零值为nil,表示没有对应的底层数组,长度和容量都为零。但是也要注意,length和capacity都是0,它们的值不一定是nil。//比较vars[]intfmt.Println(len(s)==0,s==nil)//truetrues=nilfmt.Println(len(s)==0,s==nil)//truetrues=[]int(nil)fmt.Println(len(s)==0,s==nil)//真=[]int{}fmt.Println(len(s)==0,s==nil)//真假所以判断slice是否为空,使用内置函数len,而不是判断是否为nil。要附加元素,请使用内置函数append。//附加s5:=append(s4,9)fmt.Printf("s5:%v\n",s5)//s5:[123456789]s6:=append(s4,10,11)fmt.Printf("s6:%v\n",s6)//s5:[123456781011]添加另一个切片需要在另一个切片后添加三个点。//追加另一个切片s7:=[]int{12,13}s7=append(s7,s6...)fmt.Printf("s7:%v\n",s7)//s7:[1213123456781011]copy//复制s8:=[]int{1,2,3,4,5}s9:=[]int{5,4,3}s10:=[]int{6}copy(s8,s9)fmt.Printf("s8:%v\n",s8)//s8:[54345]copy(s10,s9)fmt.Printf("s10:%v\n",s10)//s10:[5]引用类型上面介绍数组的时候说过,数组是值类型,所以传参的时候会复制整个数组内容。如果数组很大,会极大地影响性能。传递切片只是复制切片本身,不影响底层数组,效率很高。packagemainimport"fmt"funcmain(){s9:=[]int{5,4,3}//传参modify(s9)fmt.Println("main:",s9)//main:[3043]}funcmodify(a[]int){a[0]=30fmt.Println("modify:",a)//modify:[3043]}modify中修改的值会影响main。总结在本文中,您学习了前两种复合数据类型:数组和切片。分别介绍了函数的创建、常用操作和函数间的传递。数组的长度是固定的,这是切片的基础;slice的长度是可变的,还有一个额外的capacity属性,指针指向的底层结构是数组。在函数传参的过程中,如果数组很大,会大大影响效率,而slice解决了这个问题,效率更高。在日常开发中,切片的使用频率更高。本文转载自微信公众号「AlwaysBeta」,可通过以下二维码关注。转载本文请联系AlwaysBeta公众号。
