这一节开始就来聊聊Go中的容器类型了,作为一个学Java的表示狂喜,学过Java的同学一定知道Java中容器类型之复杂(各种集合框架),哈哈。
定义数组
和Java中数组一样,Go中的数组依然是固定长度,同一数据类型,自动填充默认值的特点。下标当然也是从0开始。
声明方式1,固定长度,在指定位置赋值:
var arr1 [10]int
arr[0]=100
arr[3]=200
fmt.Println(arr1)
结果:
[100 0 0 200 0 0 0 0 0 0]
这里发现Go中的print方法不同于java中print,可以直接打印数组的内容,在java中我们还需要使用Arrays工具类对其进行tostring后才可以输出。
我们也可以在声明时就赋好值:
var arr2 [10]int=[10]int{1,2,3,4,5}
此时我们对数组arr2的前5个位置赋值,基于类型推断的特点,我们可以将前面的长度以及数据类型都省去:
var arr2=[10]int{1,2,3,4,5}
这次我们遍历输出它的值和地址,观察其特点:
for i:=0;i<len(arr2);i++{
fmt.Printf("arr[%d]=%v,地址=%p\n",i,arr2[i],&arr[i])
}
结果:
array2[0]=1,地址=0x1400001c140
array2[1]=2,地址=0x1400001c148
array2[2]=3,地址=0x1400001c150
array2[3]=4,地址=0x1400001c158
array2[4]=5,地址=0x1400001c160
array2[5]=0,地址=0x1400001c168
array2[6]=0,地址=0x1400001c170
array2[7]=0,地址=0x1400001c178
array2[8]=0,地址=0x1400001c180
array2[9]=0,地址=0x1400001c188
稍加观察就会发现,数组中存储的各元素内存地址是连续的
同样我们还可以使用之前遍历字符串时提到的for range方式遍历:
for index,value := range arr2{
fmt.Printf("arr[%d]=%v,地址=%p\n",index,value,&arr2[index])
}
声明方式2,固定长度并赋值:
arr3:=[...]int{1,2,3,4,5}
fmt.Println(arr3,len(arr3))
此种方式创建出来的数组长度就是我们赋值的个数,所以此时的arr3和arr2并不相等,因为长度也是数组的组成部分:
[1 2 3 4 5] 5
声明方式3,指定范围赋值:
arr4:=[...]int{10:200,15:500}
fmt.Println(len(arr4))
fmt.Println(arr4)
这种声明方式我们对指定下标开始赋值到指定下标赋值结束,上面的代码我们对arr4的第10个下标上赋值,以及第15个下标上赋值,那么会得到长度16,10和15位置上的值为200、500的这样一个数组:
16
[0 0 0 0 0 0 0 0 0 0 200 0 0 0 0 500]
当我们只对一个下标赋值时,就代表为这个数组的最后一个位置赋值了:
arr4:=[...]int{10:200}
fmt.Println(len(arr4))
fmt.Println(arr4)
11
[0 0 0 0 0 0 0 0 0 0 200]
之前聊到了指针类型,那么在这里稍微提一个比较特殊的,Go中的数组是值类型,为了验证,我们对arr5中的一个值进行改变,此时我们声明一个函数,用来替换arr5中第三个元素的值,并输出它,关于函数的相关我们后面再讲:
func changeArr(arr[10]int){
arr[3]=100
fmt.Println("改变后的数组:",arr)
}
该函数的作用就是改变数组中第三个位置上的数成100,接下来声明一个数组并调用它:
arr5:=[10]int{1,2,3}
fmt.Println("调用函数前:",arr5)
changeArr(arr5)
fmt.Println("调用函数后:",arr5)
调用函数前: [1 2 3 0 0 0 0 0 0 0]
改变后的数组: [1 2 3 100 0 0 0 0 0 0]
调用函数后: [1 2 3 0 0 0 0 0 0 0]
可以看到,在函数中作为参数的arr5的确改变了,但调用方法完,arr5的本身并没有得到改变。因为数组是值类型,当把它传递给一个时,实际上复制了一个,因此在函数中操作的是它的拷贝,而并非它本身。因此就出现了上面的结果。
而在上一节聊到了指针,它存储的是指向变量的内存地址,那么通过它我们就可以改变该变量的值,接下来看,我们再次定义一个函数,这次传入的参数是一个数组指针类型:
func changeArrByPointer(arr*[10] int){
(*arr)[0]=100;
}
注意数组类型的指针取值时需要带括号,再次调用该函数:
arr5:=[10]int{1,2,3}
fmt.Println("调用函数前:",arr5)
changeArrByPointer(&arr5)
fmt.Println("调用函数后:",arr5)
调用函数后: [1 2 3 0 0 0 0 0 0 0]
调用函数前: [1 2 3 0 0 0 0 0 0 0]
调用函数后: [100 2 3 0 0 0 0 0 0 0]
改变成功。
费了老大劲写了两个函数改变这个数组中的值,就是为了提醒各位,Go中的数组是值类型。使用指针的方式可以提高程序的效率,因为当传递指针时不会产生新的副本,节省下了一部分空间,特别是当数组很大时,使用指针不失为一种好方案,但还是得结合实际需求去使用。
二维数组
二维数组的概念就不多提了,大家可以理解为一个棋盘,定位其中的一个元素需要两个坐标,而这两个坐标就是二维数组的两个数组中的位置:
var arr6 [4][2] int //声明一个4行2列的数组
fmt.Println(arr6)
arr7:=[4][2]int{{10,11},{3,5},{2,3},{100,88}} //为二维数组的每个元素赋值
fmt.Println(arr7)
arr8:=[4][2]int{1:{100,90},3:{8,9}}//对指定下标赋值
fmt.Println(arr8)
[[0 0] [0 0] [0 0] [0 0]]
[[10 11] [3 5] [2 3] [100 88]]
[[0 0] [100 90] [0 0] [8 9]]