Go中的数组

436 阅读5分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

数组

Go语言中的数组是具有相同类型, 有编号的, 长度固定的一组数据.

数组中的元素可以是布尔, 整型, 字符串, 也可以是自定义类型

数组的长度是一个非负正整数的常量

数组的下标从零开始

Go中的数组是一个值类型, 而不是一个对象类型或者引用类型

数组的类型包括数组的长度. 如果存储的类型一样, 但是长度不一样,那么它们也不是同一种类型

数组的定义

定义数组不赋值

数组在被定义时, 会将数组中各元素的值, 初始化为它们的零值

大家可以查看前面的文章: Go中的数据类型

// 定义整型数组
// 输出: [3]int{0, 0, 0}
var list [3]int
fmt.Printf("%#v\n", list)

// 定义字符串数组
// 输出: [3]string{"", "", ""}
var names [3]string
fmt.Printf("%#v\n", names)

定义不同类型的数组

// 定义一个整型数组
ages := [5]int {11, 22, 33, 44, 55}
fmt.Println(ages)

// 定义一个浮点型数组
prices := [3]float64 {1.3, 3.5, 5.7}
fmt.Println(prices)

// 定义一个字符串数组
bookNames := [3]string {"ThreeBody1", "ThreeBody2", "ThreeBody3"}
fmt.Println(bookNames)

不指定数组长度的定义

// 不指明数组数量, 由后面数组数量自动推断
// 输出: 数组: [3]int{1, 3, 5}
arr := [...]int {1, 3, 5}
fmt.Printf("数组: %#v\n", arr)

指定位置元素的值

// 指定位置元素的值
// 输出: 数组: [4]int{8, 6, 4, 2}
arr2 := [...]int {0:8, 1:6, 2:4, 3:2}
fmt.Printf("数组: %#v\n", arr2)

// 按顺序初始化数组中的几个元素, 再指定特定位置的元素的值
	/*
	如果没有明确元素个数, 由指定位置决定数组长度
	输出: [12]int{1, 3, 5, 0, 0, 0, 0, 2, 0, 4, 0, 6}
	*/
	arr3 := [...]int {1, 3, 5, 7:2, 9:4, 11:6}
	fmt.Printf("数组: %#v\n", arr3)

使用new来创建一个数组

// 使用new来创建一个数组
// 输出: &[3]int{0, 0, 0}
newArr := new([3]int)
fmt.Printf("%#v\n", newArr)

使用new来创建一个数组, 和上面其它的方式, 又有什么区别呢? &[3]int{0, 0, 0} [3]int{0, 0, 0} 这两种类型它不是同一种类型, 一个是&[3]int, 一个是[3]int 使用new创建出来的数组, 在打印它的类型的时候, 多了&表明它返回的是一个地址,

而不使用new来创建的数组, 返回的是数组这个值

list1 := new([3]int)
list2 := list1

list2[1] = 10
fmt.Println(list1)
fmt.Println(list2) // 输出结果一样, 证明list1和list2操作的是同一个数组

var list3 [3]int
list4 := list3

list4[2] = 200
fmt.Println(list3)
fmt.Println(list4) // 输出结果不一样, 证明list3和list4是不同的数组

数组作为函数参数

// 数组作为函数参数
/*
当数组作为函数参数的时候, 会进行值传递, 所以会浪费大量内存, 一般不建义这样使用
*/
intArr := [3]int {1, 3, 5}
fmt.Printf("main函数内调用changeArr函数之前: %#v\n",arr)
changeArr(intArr)
fmt.Printf("main函数内调用changeArr函数之后: %#v\n",arr)

func changeArr(arr [3]int)  {
	arr[1] = 100
	// 函数内
	fmt.Printf("changeArr函数内更改数组值之后: %#v\n",arr)
}

数组指针作为函数参数

// 使用数组指针作为函数参数
intArr2 := new([3]int)
intArr2[0] = 2
intArr2[1] = 4
intArr2[2] = 6

fmt.Printf("main函数内调用changeArr2函数之前: %#v\n",intArr2)
changeArr2(intArr2)
fmt.Printf("main函数内调用changeArr2函数之后: %#v\n",intArr2)

func changeArr2(arr *[3]int)  {
	arr[1] = 100
	// 函数内
	fmt.Printf("changeArr2函数内更改数组值之后: %#v\n",arr)
}

Go语言中的多维数组

// 二组数组, 类型: [2][3]int
twoGroupArr := [2][3]int {{1, 3, 5}, {2, 4, 6}}
fmt.Println(twoGroupArr)

// 二组数组的长度
// 多维数组的长度只返回第一维的长度
fmt.Println(len(twoGroupArr))

// 三维数组, 类型 : [3][2][4]int
threeGroupArr := [3][2][4]int {
   {
      {1, 2, 3, 4},
      {},
   },
   {
      {},
      {5, 6, 7, 8},
   },
}
fmt.Println(threeGroupArr)

// 三组数组的长度
// 多维数组的长度只返回第一维的长度
fmt.Println(len(threeGroupArr))

...在多维数组中的使用

// ...只能在第一维使用
twoGroupArr2 := [...][3]int {{1, 2, 3}, {3, 4, 5}}
fmt.Printf("%#v\n", twoGroupArr2)

数组的访问

一组数组的访问

// 一组数组的访问
oneArr := [...]int {1, 3, 5, 7, 9}

// 通过下标访问, 从0开始, 最大下标: 数组长度-1
oneArr[1] = 10
oneArr[4] = 100

// 使用for循环访问一维数组
for i := 0; i < len(oneArr); i++ {
   fmt.Println(oneArr[i])
}

// 使用for...range访问一维数组
for i, v := range oneArr {
   fmt.Println(i, v)
}

多维数组的访问

// 多维数组的访问
twoArr := [2][3]int {{1, 3, 5}, {2, 4, 6}}

// 通过下标访问
fmt.Println("第1行的第2个元素: ",twoArr[0][1])
fmt.Println("第2行的第3个元素: ", twoArr[1][2])

// for循环访问多维数组
for i := 0; i < len(twoArr); i++ {
   for j := 0; j < len(twoArr[0]); j++ {
      fmt.Printf("%v ", twoArr[i][j])
   }
   fmt.Println()
}

// for...range访问多维数组
for _, v := range twoArr {
   for _, v2 := range v {
      fmt.Printf("%v ", v2)
   }
   fmt.Println()
}

总结

Go中的数组, 好像跟其它的数组有那么点不一样, 用起来不是很灵活, 类型固定, 数组的类型包括它的长度. 当作为参数的时候, 它是值传递, 也就是在函数调用的时候, 还是会开辟一块同样大小的空间, 将数组的值拷贝过去, 造成浪费. 如果只是在代码块内部使用, 并且已经确定数组的长度, 那就可以使用, 否则请使用切片!!!