【从零开始学习Go语言 | 第七篇】Go语言基础之数组
目录前言数组的定义数组的初始化方法一声明后单独赋值方法二 使用数组字面量直接初始化方法三简短声明类型推导访问和修改数组元素获取数组长度遍历数组传统 for 循环使用 for range多维数组声明和初始化二维数组访问二维数组元素遍历二维数组数组是值类型 —— 非常重要的特性示例1数组赋值是拷贝示例2函数传参是值拷贝注意性能数组的比较数组的局限性总结前言在编程中我们经常需要处理一组相同类型的数据比如一个班级的50个学生成绩、一周七天的气温记录等。如果为每个数据单独声明一个变量代码会变得冗长且难以维护。这时候数组就派上了用场。数组是Go语言中最基本的内置数据结构之一。它就像一排连续的盒子每个盒子里放着一个相同类型的值并且每个盒子上都标有编号索引方便我们快速找到并操作里面的内容。今天我们就来系统学习Go语言中的数组包括它的声明、初始化、遍历、多维数组以及一些重要的特性比如“值类型”。让我们一起从零开始掌握数组的使用。数组的定义数组是同一种数据类型元素的集合。 在Go语言中数组从声明时就确定使用时可以修改数组成员但是数组大小不可变化。 基本语法// 定义一个长度为3元素类型为int的数组a var a [3]int在Go语言中数组是一个固定长度、包含相同数据类型元素的序列。它的长度是数组类型的一部分这意味着[5]int和[10]int是两种完全不同的类型。语法格式var 数组名 [长度]元素类型示例var scores [5]int // 声明一个长度为5元素类型为int的数组初始值为[0 0 0 0 0] var flags [3]bool // 长度为3的bool数组初始值为[false false false] var names [2]string // 长度为2的string数组初始值为[ ]一旦声明数组的长度就固定了不能动态改变。数组的初始化数组的初始化也有很多方式。方法一声明后单独赋值var days [7]string days[0] Monday days[1] Tuesday // ... 依次赋值方法二 使用数组字面量直接初始化可以在声明时直接给数组赋初值// 方式一完整列出所有元素 var scores [5]int{98, 85, 92, 78, 88} // 方式二让编译器自动推断长度使用... var scores [...]int{98, 85, 92, 78, 88} // 长度推断为5 // 方式三指定索引初始化未指定的为零值 var fruits [5]string{0: apple, 2: banana, 4: orange} // 结果[apple, , banana, , orange]方法三简短声明类型推导在函数内部可以使用:进行简短声明func main() { nums : [3]int{1, 2, 3} fmt.Println(nums) // [1 2 3] }访问和修改数组元素通过索引下标来访问或修改数组中的元素索引从0开始最大索引为len(array)-1。func main() { arr : [4]int{10, 20, 30, 40} fmt.Println(arr[0]) // 输出10 fmt.Println(arr[2]) // 输出30 arr[1] 99 // 修改索引为1的元素 fmt.Println(arr) // 输出[10 99 30 40] }如果索引超出范围例如arr[4]程序会在运行时发生panic。获取数组长度使用内置函数len()可以获取数组的长度arr : [3]float64{1.1, 2.2, 3.3} fmt.Println(len(arr)) // 输出3遍历数组遍历数组通常有两种方式for循环通过索引遍历或者for range遍历。传统 for 循环arr : [5]int{2, 4, 6, 8, 10} for i : 0; i len(arr); i { fmt.Printf(arr[%d] %d\n, i, arr[i]) }使用 for rangerange会返回两个值索引和元素的副本。arr : [5]int{2, 4, 6, 8, 10} for index, value : range arr { fmt.Printf(索引: %d, 值: %d\n, index, value) } // 如果只需要值可以用下划线忽略索引 for _, value : range arr { fmt.Println(value) } // 如果只需要索引 for index : range arr { fmt.Println(index) }for range的写法更加简洁是Go语言中推荐的遍历方式。多维数组数组的元素可以是任意类型因此也可以是数组这样就形成了多维数组。最常用的是二维数组类似表格。声明和初始化二维数组// 声明一个3行2列的二维整型数组 var matrix [3][2]int // 初始化 var matrix [3][2]int{ {1, 2}, {3, 4}, {5, 6}, } // 部分初始化未指定的元素为零值 var matrix [3][2]int{ {1}, // 第一行[1, 0] {3, 4}, // 第二行[3, 4] {5}, // 第三行[5, 0] }访问二维数组元素value : matrix[1][1] // 第2行第2列得到4 matrix[0][0] 100 // 修改第1行第1列为100遍历二维数组使用嵌套循环或嵌套rangematrix : [2][3]int{{1,2,3}, {4,5,6}} for i, row : range matrix { for j, val : range row { fmt.Printf(matrix[%d][%d]%d , i, j, val) } fmt.Println() }注意多维数组只有第一层可以使用...来让编译器推导数组长度。例如//支持的写法 a : [...][2]string{ {北京, 上海}, {广州, 深圳}, {成都, 重庆}, } //不支持多维数组的内层使用... b : [3][...]string{ {北京, 上海}, {广州, 深圳}, {成都, 重庆}, }数组是值类型 —— 非常重要的特性值类型意味着当你把一个数组赋值给另一个数组变量或者将数组作为参数传递给函数时会完整地复制整个数组的所有元素。修改副本不会影响原始数组。这一点与C语言数组名通常退化为指针或Java数组是引用类型有本质区别需要特别留意。示例1数组赋值是拷贝a : [3]int{1, 2, 3} b : a // 拷贝 a 的所有元素到 b b[0] 100 fmt.Println(a) // 输出 [1 2 3]a 没有变化 fmt.Println(b) // 输出 [100 2 3]示例2函数传参是值拷贝注意性能func modify(arr [3]int) { arr[0] 999 } func main() { nums : [3]int{1, 2, 3} modify(nums) fmt.Println(nums) // 仍然输出 [1 2 3]因为modify操作的是副本 }如果数组很大比如长度为10000每次传参都会产生完整的拷贝可能影响性能。这时候通常使用切片slice来代替切片是引用类型传递时只复制一个描述符。后续篇章会详细介绍切片。数组的比较因为数组的长度和元素类型都是类型的一部分所以相同类型的数组可以直接使用或!进行比较比较的前提是数组元素类型支持比较如int、string、bool等切片和map不支持直接比较。比较规则两个数组相等当且仅当它们的每个对应元素都相等。a : [2]int{1, 2} b : [2]int{1, 2} c : [2]int{1, 3} fmt.Println(a b) // true fmt.Println(a c) // false // 错误示范不同类型不能比较 // d : [3]int{1,2,3} // fmt.Println(a d) // 编译错误mismatched types [2]int and [3]int数组的局限性数组在Go语言中很有用但它的固定长度使得它在很多场景下不够灵活长度固定无法动态增加或删除元素。值拷贝开销作为函数参数传递时大数组会导致明显的性能问题。不能改变大小比如需要一个可增长的列表时数组就不适用了。为了解决这些问题Go提供了切片Slice它基于数组构建但提供了动态大小、引用语义等便捷特性。在实际开发中切片的使用频率远高于数组。我们将在下一篇文章中详细学习切片。不过数组作为切片的基础理解数组对于掌握切片至关重要。总结今天我们学习了Go语言中的数组定义var arr [长度]类型长度是类型的一部分。初始化可以使用字面量、指定索引、自动推断长度等方式。访问和修改通过arr[索引]索引从0开始。长度len(arr)。遍历for循环或更优雅的for range。多维数组元素为数组的数组通常用嵌套循环遍历。值类型赋值和传参都会复制整个数组修改副本不影响原数组。比较相同类型的数组可以直接使用进行比较。局限性长度固定、值拷贝开销大实际开发中更常用切片。数组是Go语言数据结构的基础理解它会让你后续学习切片、映射等类型事半功倍。下一篇文章我们将进入切片Slice的世界看看它是如何优雅地解决数组的不足的。如果你对今天的讲解有任何疑问或者想进一步探索欢迎在评论区留言。坚持从零开始我们一步步掌握Go语言感谢阅读我们下篇再见如果我的内容对你有帮助请点赞评论收藏。创作不易大家的支持就是我坚持下去的动力