Go语言基础之数组
1 数组概述
数组是由一个固定长度、相同类型元素组成的连续序列,一个数组可以由零个或多个元素组成。数组存储的类型可以是内置类型,如整型或者字符串,也可以是某种结构类型。由于数组只能存储固定长度的相同类型的数据,所以在Go语言中很少直接使用。
下图是数组的内部实现,其中方框内的代表数组里的元素,数组中的元素存储在一段连续的内存空间中,且每个元素可以用一个唯一的索引(下标)来访问,这样就可以以固定的速度索引数组中的任意数据,速度非常快。
图1 数组的内部实现
数组的下标是从0开始的,最后一个元素的下标是len-1,也就是数组的长度(元素个数)减1,如果访下标在合法范围之外,则触发访问越界,会panic。
2 数组声明和初始化
2.1 数组声明
1 var 数量变量名 [元素个数]Type
比如: var a [8]int ,数组的长度必须是常量,并且长度是数组类型的一部分,长度是数组的一个属性,一旦定义数组的长度不能变。于是 [5]int 和 [8]int 不是同一个类型。
1 var a [5]int 2 var b [8]int 3 a = b //不能这样使用,因为a和b不是同一个类型
2.2 数组初始化
数组的初始化有很多种方式:
方式一:
使用初始化列表来设置数组元素的值
1 func main() { 2 var array1 [3]int //编译器会自动为数组初始化int类型的默认0值 3 var numArray = [3]int{1, 2} //指定初始值完成初始化 4 var cityArray = [3]string{"西安", "成都", "上海"} 5 fmt.Println(array1) //[0 0 0] 6 fmt.Println(numArray) //[1 2] 7 fmt.Println(cityArray) //[西安 成都 上海] 8 }
方式二:
方式一的弊端:每次声明的数组为指定的长度的数组,而后要确保提供的初始值的个数和数组长度一致。那么方式二可以让编译器根据初始值的个数自行推断数组的长度,也就是自动计算声明数组的长度。代码如下:
1 func main() { 2 var testArray [3]int 3 var numArray = [...]int{1, 2} 4 var cityArray = [...]string{"西安", "上海"} 5 6 fmt.Println(testArray) //[0 0 0] 7 fmt.Println(numArray) //[1 2] 8 fmt.Printf("numArray的类型是:%T\n", numArray) //numArray的类型是:[2]int 9 fmt.Println(cityArray) //[西安 上海] 10 fmt.Printf("cityArray的类型是:%T\n", cityArray) //cityArray的类型是:[2]string 11 }
方式三:
声明数组并指定特定元素的值
1 func main() { 2 a := [...]int{1: 1, 10: 10} 3 fmt.Println(a) //[0 1 0 0 0 0 0 0 0 0 10] 4 fmt.Printf("a的类型是:%T", a) //a的类型是:[11]int 5 }
2 数组的遍历
2.1 for循环遍历
1 func main() { 2 //for循环遍历 3 var cityArray = [...]string{"北京", "上海", "西安"} 4 5 for i := 0; i < len(cityArray); i++ { 6 fmt.Printf("cityArray[%d]=%v\n", i, cityArray[i]) 7 } 8 }
2.2 for range遍历
这是Go语言一种独有的结构,可以用来遍历访问数组的元素。
for-range的基本语法
1 for index, value := range array01 { 2 ... 3 }
说明:
- 第一个返回值index是数组的下标
- 第二个value是在该下标位置的值
- index和value仅在for循环内部可见的局部变量
- 遍历数组元素的时候,如果不想使用下标index,可以直接把下标index标为下划线_
- index和value的名称不是固定的,即程序员可以自行指定,一般命名为index和value
for-range的案例
1 func main() { 2 //演示for-range遍历数组 3 heroes := [...]string{"钢铁侠", "蜘蛛侠", "绿巨人"} 4 5 for i, v := range heroes { 6 fmt.Printf("i=%v v=%v\n", i, v) 7 fmt.Printf("heroes[%d]=%v\n", i, heroes[i]) 8 } 9 10 for _, v := range heroes { 11 fmt.Printf("元素的值=%v\n", v) 12 } 13 }
3 多维数组
Go语言是支持多维数组的,这里以二维数组为例(数组中有嵌套数组)
3.1 二维数组的定义
1 func main() { 2 a := [3][2]string{ 3 {"北京", "上海"}, 4 {"广州", "深圳"}, 5 {"成都", "重庆"}, 6 } 7 fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]] 8 fmt.Println(a[2][1]) //支持索引取值:重庆 9 }
3.2 二维数组的遍历
1 func main() { 2 a := [3][2]string{ 3 {"北京", "上海"}, 4 {"广州", "深圳"}, 5 {"成都", "重庆"}, 6 } 7 for _, v1 := range a { 8 for _, v2 := range v1 { 9 fmt.Printf("%s\t", v2) 10 } 11 fmt.Println() 12 } 13 } 14 15 // 输出 16 // 北京 上海 17 // 广州 深圳 18 // 成都 重庆
注意:多维数组只有第一层可以使用 ... 来让编译器推导数组长度,例如:
1 //支持的写法 2 a := [...][2]string{ 3 {"北京", "上海"}, 4 {"广州", "深圳"}, 5 {"成都", "重庆"}, 6 } 7 //不支持多维数组的内层使用... 8 b := [3][...]string{ 9 {"北京", "上海"}, 10 {"广州", "深圳"}, 11 {"成都", "重庆"}, 12 }
4 数组使用的注意事项和细节总结
- 数组是多个相同类型数据的组和,一个数组一旦声明/定义了,其长度是固定的,不能动态变化
- 数组中的元素可以是任何数据类型,包括值类型和引用类型,但不能混用
- 数组创建后,如果没有赋值,有默认值(零值)
1 func main() { 2 // 1. 数值(整数系列,浮点数系列):0 3 // 2. 字符串:"" 4 // 3. 布尔类型:false 5 6 var arr01 [3]float32 7 var arr02 [3]string 8 var arr03 [3]bool 9 fmt.Printf("arr01=%v arr02=%v arr03=%v", arr01, arr02, arr03) 10 }
- 用数组的步骤:声明数组并开辟空间;给数组各个元素赋值(默认零值);使用数组
- 数组的小标是从0开始的
1 var arr [3]string 2 var index int = 3 3 arr[index] = "tom" //因为下标是0-2,因此arr[3]就越界
- 数组下标必须在指定范围内使用,否则报panic;数组越界
- Go的数组属值类型,在默认情况下是值传递,因此会进行值拷贝,数组间不会相互影响
1 //修改数组第一个下标对应的值 2 func test(arr [3]int) { 3 arr[0] = 0 4 fmt.Println("test arr:", arr) 5 } 6 7 func main() { 8 arr := [3]int{10, 20, 30} 9 test(arr) 10 fmt.Println("main arr:", arr) 11 }
- 如果想在其它函数中,去修改原来的数组,可以使用引用指针(指针方式)
1 //修改数组第一个下标对应的值 2 func test(arr *[3]int) { 3 (*arr)[0] = 0 4 // arr[0] = 0 5 fmt.Println("test arr:", *arr) 6 } 7 8 func main() { 9 arr := [3]int{10, 20, 30} 10 test(&arr) 11 fmt.Println("main arr:", arr) 12 }
- 长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度,看下面案例
- 数组支持 “==“、”!=” 操作符,因为内存总是被初始化过的。
- [n]*T表示指针数组,*[n]T表示数组指针 。
5 数组的应用案例
1、创建一个byte类型的26个元素的数组,分别放置'A'-'Z'。使用for循环访问所有元素并打印出来。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 func main() { 2 // 思路 3 // 1. 声明一个数组 var myChars [26]byte 4 // 2. 使用for循环,利用字符可以进行运算的特点来赋值 5 // 3. for循环打印 6 var myChars [26]byte 7 8 for i := 0; i < 26; i++ { 9 myChars[i] = 'A' + byte(i) // 需要将i转换为byte类型 10 } 11 12 for i := range myChars { 13 fmt.Printf("%c", myChars[i]) 14 } 15 }
2、请求出一个数组的最大值,并得到对应的下标
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 func main() { 2 // 思路 3 // 1. 随机生成5个随机数,将其存储在intArr数组中 4 // 2. 假设下标为0的数最大 5 // 3. 然后依次向后比较,如果发现更大,则交换 6 var intArr [5]int 7 len := len(intArr) 8 rand.Seed(time.Now().UnixNano()) 9 for i := 0; i < len; i++ { 10 intArr[i] = rand.Intn(100) 11 } 12 13 maxVal := intArr[0] 14 maxValIndex := 0 15 16 for i := 1; i < len; i++ { 17 if maxVal < intArr[i] { 18 maxVal = intArr[i] 19 maxValIndex = i 20 } 21 } 22 fmt.Println("intArr:", intArr) 23 fmt.Printf("maxVal=%v maxValIndex=%v", maxVal, maxValIndex) 24 }
3、请求出一个数组的和和平均值。for-range
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 func main() { 2 var intArr [5]int 3 len := len(intArr) 4 5 rand.Seed(time.Now().UnixNano()) 6 for i := 0; i < len; i++ { 7 intArr[i] = rand.Intn(100) 8 } 9 10 fmt.Println("intArr:", intArr) 11 12 sum := 0 13 for _, val := range intArr { 14 sum += val 15 } 16 17 // 如何让平均值保留到小数 18 fmt.Printf("sum=%v 平均值=%v\n", sum, float64(sum)/float64(len)) 19 }
4、随机生成五个数,并将其反转打印
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 func main() { 2 // 思路 3 // 1. 随机生成五个数,rand.Intn() 函数 4 // 2. 将随机数放入一个int数组内 5 // 3. 反转打印,交换的次数是 len /2 ,倒数第一个和第一个元素交换 6 var nums [5]int 7 len := len(nums) 8 9 // 为了每次生成的随机数不一样,需要给一个seed值 10 rand.Seed(time.Now().UnixNano()) 11 for i := 0; i < len; i++ { 12 nums[i] = rand.Intn(100) // 0 <= n < 100 13 } 14 15 fmt.Println("交换前:", nums) 16 17 temp := 0 18 for i := 0; i < len/2; i++ { 19 temp = nums[len-1-i] 20 nums[len-1-i] = nums[i] 21 nums[i] = temp 22 } 23 fmt.Println("交换后:", nums) 24 }
5、找出数组中和为指定值得两个元素的下标,比如从数组[1, 3, 5, 7, 8]中找出和为8的两个元素的下标分别为(0, 3)和(1, 2)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 func main() { 2 var nums = [...]int{1, 3, 5, 7, 8} 3 for i := 0; i < len(nums); i++ { 4 for j := i + 1; j < len(nums); j++ { 5 if nums[i]+nums[j] == 8 { 6 fmt.Printf("(%d, %d)\n", i, j) 7 } 8 } 9 } 10 }