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
", numArray)     //numArray的类型是:[2]int
 9     fmt.Println(cityArray)                           //[西安 上海]
10     fmt.Printf("cityArray的类型是:%T
", 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
", i, cityArray[i])
7     }
8 }

2.2 for range遍历

  这是Go语言一种独有的结构,可以用来遍历访问数组的元素。

for-range的基本语法

1 for index, value := range array01 {
2     ...
3 }

说明:

  1. 第一个返回值index是数组的下标
  2. 第二个value是在该下标位置的值
  3. index和value仅在for循环内部可见的局部变量
  4. 遍历数组元素的时候,如果不想使用下标index,可以直接把下标index标为下划线_
  5. 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
", i, v)
 7         fmt.Printf("heroes[%d]=%v
", i, heroes[i])
 8     }
 9 
10     for _, v := range heroes {
11         fmt.Printf("元素的值=%v
", 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	", 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循环访问所有元素并打印出来。

 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 }
View Code

  2、请求出一个数组的最大值,并得到对应的下标

 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 }
View Code

  3、请求出一个数组的和和平均值。for-range

 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
", sum, float64(sum)/float64(len))
19 }
View Code

  4、随机生成五个数,并将其反转打印

 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 }
View Code

  5、找出数组中和为指定值得两个元素的下标,比如从数组[1, 3, 5, 7, 8]中找出和为8的两个元素的下标分别为(0, 3)和(1, 2)。

 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)
", i, j)
 7             }
 8         }
 9     }
10 }
View Code
原文地址:https://www.cnblogs.com/dabric/p/12272815.html