前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >9. Go复合类型-数组

9. Go复合类型-数组

作者头像
Devops海洋的渔夫
发布2021-07-29 10:23:08
4950
发布2021-07-29 10:23:08
举报
文章被收录于专栏:Devops专栏Devops专栏

9. Go复合类型-数组

前言

前面我们已经学习了一些简单的基本类型,现在学习复合类型,复合类型主要包括了数组,指针,切片,结构体等。现在先来学习数组.

1:数组

如果要存储班级里所有学生的数学成绩,应该怎样存储呢?可能有同学说,通过定义变量来存储。但是,问题是班级有80个学生,那么要定义80个变量吗?

像以上情况,最好是通过数组的方式来存储。

所谓的数组:是指一系列同一类型数据的集合。

1.1 数组定义

代码语言:javascript
复制
var a [10]int 

数组定义也是通过var 关键字,后面是数组的名字a,长度是10,类型是整型。表示:数组a能够存储10个整型数字。也就是说,数组a的长度是10。

我们可以通过len( )函数测试数组的长度,如下所示:

代码语言:javascript
复制
func main() {
   var a [10]int
   fmt.Println(len(a))
}

执行如下:

代码语言:javascript
复制
$ go run 01_数组.go
10

当定义完成数组a后,就在内存中开辟了10个连续的存储空间,每个数据都存储在相应的空间内,数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数组的长度。

注意:数组的长度只能是常量。以下定义是错误的:

代码语言:javascript
复制
var n int = 10

var a [n]int

1.2 数组赋值

数组定义完成后,可以对数组进行赋值操作。

数组是通过下标来进行操作的,下标的范围是从0开始到数组长度减1的位置。

代码语言:javascript
复制
var a[10] int // 表示的范围是a[0],a[1],a[2].......,a[9]
对数组赋值的第一种方法:

如果现在给a[10]=29, 会出现什么情况呢?

可以看到报错:数组越界

代码语言:javascript
复制
# command-line-arguments
.\01_数组.go:14:3: invalid array index 10 (out of bounds for 10-element array)
.\01_数组.go:19:24: invalid array index 10 (out of bounds for 10-element array)
对数组赋值的第二种方式:

但是这种赋值方式比较麻烦,所以可以使用第二种赋值方式,如下所示:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   var a [10]int // 定义数组

   // 数组赋值
   for i := 0;  i < 10; i++ {
      a[i] = i + 1
   }
   // 打印数组的值
   for i := 0;  i < 10; i++ {
      fmt.Printf("a[%d]=%d\n", a[i], a[i])
   }
}

执行如下:

代码语言:javascript
复制
a[1]=1
a[2]=2
a[3]=3
a[4]=4
a[5]=5
a[6]=6
a[7]=7
a[8]=8
a[9]=9
a[10]=10

通过for循环完成数组的赋值与输出。注意:循环的条件,如果将循环条件修改成 i<=10是否正确。

使用 len() 函数计算数组长度,遍历打印数组的值

在上一节中,我们说过可以通过len( )函数来获取 数组的长度,所以也可以对上面的程序,进行如下的修改:

使用 range 遍历数组的值

对数组中的数据输出,也可以使用range.如下所示:

代码语言:javascript
复制
// 使用 range 遍历数组的值
for i, data := range a{
   fmt.Printf("a[%d]=%d\t", i, data)
}

输出如下:

代码语言:javascript
复制
a[0]=1  a[1]=2  a[2]=3  a[3]=4  a[4]=5  a[5]=6  a[6]=7  a[7]=8  a[8]=9  a[9]=10

i 变量存储的是数组的下标,data变量存储的是数组中的值。

如果只想输出数组中的元素值,不希望输出下标,可以使用匿名变量 _

代码语言:javascript
复制
// 使用 range 遍历数组的值
for _, data := range a{ // 使用匿名变量 _ 
   //fmt.Printf("a[%d]=%d\t", i, data)
   fmt.Printf("元素的值=%d\t", data)
}
数组的默认值

上面的案例中,首先完成了数组的赋值,然后再输出数组中的值。但是,如果定义完成数组后,没有赋值,直接输出会出现什么样的问题呢?

代码语言:javascript
复制
var a [10]int // 定义数组

// 使用 range 遍历数组的值
for _, data := range a{
    fmt.Printf("元素的值=%d\t", data)
}

a数组中的元素类型是整型,定义完成后,直接输出,结果全部是0.

当然数组中存储的元素类型也可以是其它类型,如下所示:

代码语言:javascript
复制
var a [10]float64 //如果不赋值,直接输出,结果默认全部是0

var a [10]string //如果不赋值,直接输出,结果默认全部是空字符

var a [10]bool //如果不赋值,直接输出,结果默认全部是false.

1.3 数组初始化

上一小节中,首先先定义数组,然后再完成数组的赋值。其实,在定义数组时,也可以完成赋值,这种情况叫做数组的初始化。

具体案例如下:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   //1. 全部初始化
   var a [5]int = [5]int{1, 2, 3, 4, 5}
   fmt.Println("a = ", a)

   b := [5]int{1, 2, 3, 4, 5}
   fmt.Println("b = ", b)

   // 部分初始化,没有初始化的元素,自动赋值为 0
   c := [5]int{1, 2, 3}
   fmt.Println("c = ", c)

   // 指定某个元素初始化
   d := [5]int{2:10, 4:20} // 设置下标2的值为10,下标4的值为20
   fmt.Println("d = ", d)
}

执行如下:

代码语言:javascript
复制
a =  [1 2 3 4 5]
b =  [1 2 3 4 5]
c =  [1 2 3 0 0]
d =  [0 0 10 0 20]

还可以使用 ... 设置数组的长度,通过初始化确定数组的长度。

代码语言:javascript
复制
b := [...]int{1, 2, 3, 4, 5} // 通过...设置数组的长度
fmt.Println("b = ", b)

1.4 数组练习

练习1:从一个整数数组中取出最大的整数,最小整数,总和,平均值。

代码如下:

代码语言:javascript
复制
package main

import "fmt"

func main() {
 //1.全部初始化
 var a [6]int = [6]int{1, 2, 3, 4, 5, 0}
 //2.声明两个变量来存储最大值与最小值
 var max int
 var min int
 var sum int // 存储和
 // 循环数组,将数组中的每个元素值与最大值和最小值进行比较
 for i := 0; i < len(a); i++ {
  if a[i] > max {
   max = a[i]
  }
  if a[i] < min {
   min = a[i]
  }
  sum += a[i]
 }
 fmt.Printf("最大值: %d, 最小值: %d, 和为: %d, 平均值: %d", max, min, sum, sum/len(a))
}

以上程序输出的结果是:

代码语言:javascript
复制
最大值: 5, 最小值: 0, 和为: 15, 平均值: 2

通过观察发现该程序输出的结果没有问题。

但是,现在将程序进行如下修改:将数组中的0元素删除,同时将数组的长度修改为5.

思考:数组中没有0,为什么输出的结果中最小值为0呢?

现在,在将程序进行如下修改:将数组中的数据全部修改成负数。

思考:数组中没有0,为什么输出的结果中最大值为0呢?

**应该怎样解决如上的问题呢?**将程序修改如下:

image-20210507083531212

练习2:计算一个整数数组的所有元素的和。
代码语言:javascript
复制
// 练习2:计算一个整数数组的所有元素的和。
var a [6]int = [6]int{1, 2, 3, 4, 5, 0}
var sum int // 存储和
for i := 0; i < len(a); i++ {
   sum += a[i]
}
fmt.Printf("和为: %d", sum)

运行结果如下:

代码语言:javascript
复制
和为: 15
练习3:数组里面都是人的名字,分割成:例如:老王|王叔|王哥…”
代码语言:javascript
复制
// 练习3:数组里面都是人的名字,分割成:例如:老王|王叔|王哥…”
var name [3]string = [3]string{"老王", "王叔", "王哥"}
var str1 string
// 拼接字符串
for i := 0; i < len(name); i++ {
   str1 += name[i] + "|"
}
fmt.Printf(str1) 

该程序最终的输出结果:

代码语言:javascript
复制
老王|王叔|王哥|

现在将最后一个“|”去掉,程序应该怎样进行修改?

具体思路:首先通过循环的方式取出数组中前两个元素,分别链接”|” ,存储到变量str中。然后获取最后一个元素,不需要链接“|”,直接与str链接就可以了。获取names数组中最后一个元素的方式:

通过len(name)计算出数组的长度,然后减去1, 就是数组中最后一个元素的下标(数组的下标是从0开始计算)。所以取出最后一个元素的方式为:names[len(name)-1]

代码语言:javascript
复制
// 练习3:数组里面都是人的名字,分割成:例如:老王|王叔|王哥…”
var name [3]string = [3]string{"老王", "王叔", "王哥"}
var str1 string
// 拼接字符串,最拼接最后一个字符之前的所有字符
for i := 0; i < len(name)-1; i++ {
   str1 += name[i] + "|"
}
fmt.Printf(str1 + name[len(name)-1]) // 拼接最后一个字符,并打印

执行如下:

代码语言:javascript
复制
老王|王叔|王哥
练习4:将一个整数数组的每一个元素进行如下的处理:如果元素是正数则将这个位置的元素的值加1,如果元素是负数则将这个位置的元素的值减1,如果元素是0,则不变。
代码语言:javascript
复制
// 练习4:将一个整数数组的每一个元素进行如下的处理:
// 如果元素是正数则将这个位置的元素的值加1,如果元素是负数则将这个位置的元素的值减1,如果元素是0,则不变。
num := [...]int{10,8,0,7,-1,-3,-6,99}
fmt.Println("处理前的数组: ", num)

for i,v := range num{
   // 如果元素是正数则将这个位置的元素的值加1
   if v > 0 {
      num[i] += 1
   }
   // 如果元素是负数则将这个位置的元素的值减1
   if v < 0 {
      num[i] -= 1
   }
   // 如果元素是0,则不变
   if v == 0 {

   }
}
fmt.Println("处理后的数组: ", num)

执行如下:

代码语言:javascript
复制
处理前的数组:  [10 8 0 7 -1 -3 -6 99]
处理后的数组:  [11 9 0 8 -2 -4 -7 100]
练习5:将一个字符串数组的元素的顺序进行反转。{“我”,“是”,”好人”} {“好人”,”是”,”我”}。第i个和第length-i-1个进行交换。

分析如下:

image-20210507234711950

代码如下:

代码语言:javascript
复制
// 练习5:将一个字符串数组的元素的顺序进行反转。{“我”,“是”,”好人”} {“好人”,”是”,”我”}。第i个和第length-i-1个进行交换。
names := [...]string{"我", "是", "好人"}
fmt.Println("交换前的数组: ", names)
var temp string // 定义中间变量
for i := 0; i < len(names)/2; i++ {
   temp = names[i]
   names[i] = names[len(names)-1-i]
   names[len(names)-1-i] = temp
}
fmt.Println("交换后的数组: ", names)

执行如下:

代码语言:javascript
复制
交换前的数组:  [我 是 好人]
交换后的数组:  [好人 是 我]

1.5 数组冒泡排序

如何对数组中存储的数据,按照从大到小,或者从小到大进行排序?可以使用冒泡排序。

分析过程:

具体代码实现:

代码语言:javascript
复制
package main

import "fmt"

func main() {
   var num [10]int = [10]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
   var temp int
   fmt.Println("冒泡排序前的数组: ", num)
   // 冒泡排序
   for i := 0; i < len(num)-1; i++ {
      for j := 0; j < len(num)-1-j; j++ {
         if num[j] > num[j+1] {
            temp = num[j]
            num[j] = num[j+1]
            num[j+1] = temp
         }
      }
   }
   fmt.Println("冒泡排序后的数组: ", num)
}

执行如下:

代码语言:javascript
复制
冒泡排序前的数组:  [9 8 7 6 5 4 3 2 1 0]
冒泡排序后的数组:  [4 5 6 7 8 9 3 2 1 0]

1.6 数组作为函数参数

数组也可以像变量一样,作为参数传递给函数,用法如下:

代码语言:javascript
复制
package main

import "fmt"

func modify(a [5]int) { // 数组 a [5]int 作为参数
   a[0] = 666 // 修改传递过来的数组,修改第一个元素的值
   fmt.Println("modify a=", a)
}

func main() {
   a := [5]int{1, 2, 3, 4, 5} // 初始化
   modify(a)                  // 数组传递过去
   fmt.Println("main a=", a)
}

执行如下:

代码语言:javascript
复制
modify a= [666 2 3 4 5]
main a= [1 2 3 4 5]

注意:在main( )函数中,定义数组a, 然后调用modify( )方法传递数组,同时在modify( )方法中修改数组中第一个元素。最终输出结果发现,并不会影响main( )函数中数组a的值,这一点与其它编程语言是有区别的。

练习1:用方法来实现:有一个字符串数组:{ "马龙", "迈克尔乔丹", "雷吉米勒", "蒂姆邓肯", "科比布莱恩特" },请输出最长的字符串。

思路:

1:在main( )函数中定义该数组,并且传递到GetLongest( )方法中

2:定义一个max变量用来存储最长的字符串,并且假设数组中的第一个元素是最长的。

3:通过循环,将数组中的元素取出来与max变量进行比较,如果长度比max变量中存储的长度要长,赋值给max

4:将结果返回

代码语言:javascript
复制
package main

import "fmt"

// 练习1:用方法来实现:有一个字符串数组: { "马龙", "迈克尔乔丹", "雷吉米勒", "蒂姆邓肯", "科比布莱恩特" },请输出最长的字符串。
func GetLongest(names [5]string) string {
   var max string
   max = names[0]
   for i := 1; i < len(names); i++ {
      if len(names[i]) > len(max) {
         max = names[i]
      }
   }
   return max
}

func main() {
   str := [...]string{"马龙", "迈克尔乔丹", "雷吉米勒", "蒂姆邓肯", "科比布莱恩特"}
   name := GetLongest(str)
   fmt.Println(name)
}

执行如下:

代码语言:javascript
复制
科比布莱恩特
练习2:用方法来实现:请计算出一个整型数组的平均值。保留两位小数

思路:该题主要是通过循环的方式,将数组中的每个元素取出来进行累加,最后求平均数。注意输出格式

代码语言:javascript
复制
package main

import "fmt"

//练习2:用方法来实现:请计算出一个整型数组的平均值。保留两位小数
func GetAvg(numbers [3]int) float64 {
   var sum int
   for _, v := range numbers {
      sum += v
   }
   return float64(sum) / float64(len(numbers))
}

func main() {
   num := [...]int{1, 2, 7}
   avg := GetAvg(num)
   fmt.Printf("%.2f", avg)
}

执行如下:

代码语言:javascript
复制
3.33
练习3:在上一题中,计算整型数组中的平均值,这个数组中的元素都是固定死的,如果将需求修改成数组中的3个元素是随机生成的,应该怎样进行处理?这就涉及到随机数的知识:随机数,可以理解成让计算机随机生成的一组数字。

在GO语言中,产生随机数的语法如下:

代码语言:javascript
复制
package main

import (
 "fmt"
 "math/rand"
)

func RandomFunc() {
   //设置种子,只需一次
   rand.Seed(123)
   for i := 0; i < 5; i++ {
      // 产生随机数
      fmt.Println("rand = ", rand.Int())
   }
}

func main() {
   RandomFunc()
}

执行如下:

代码语言:javascript
复制
rand =  5361704182646325489
rand =  241876450138978746
rand =  2305561650894865143
rand =  5680255032342805883
rand =  2116592265311177487

要产生随机数,还需要导入math/rand包,

rand.Seed(123),设置随机种子,由于随机种子是固定的,所以运行该程序,发现每次生成的随机数是一样的。

“所谓的随机种子:随机数是由随机种子根据一定的计算方法计算出来的数值。所以,只要计算方法一定,随机种子一定,那么产生的随机数就不会变。 为了解决这个问题,可以让随机种子随时发生变化,所以可以利用时间来作为随机种子。如果用时间来做随机种子,需要导入“time”包。 ”

rand.Int( );每次产生的整型随机数都非常的大,所以可以限制范围,使用的是rand中的Intn()方法。例如:rand.Intn(100),限制100内的随机数。

最终程序可以进行如下的修改:

代码语言:javascript
复制
package main

import (
 "fmt"
 "math/rand"
 "time"
)

func RandomFunc() {
   //设置种子,只需一次
   //rand.Seed(123)
   rand.Seed(time.Now().UnixNano()) // 以当前系统时间作为种子参数
   for i := 0; i < 5; i++ {
      // 产生随机数
      //fmt.Println("rand = ", rand.Int())
      fmt.Println("rand = ", rand.Intn(100)) // 限制在100内的数
   }
}

func main() {
   RandomFunc()
}

最终,完整代码如下,该程序中涉及到了数组,函数嵌套调用,随机数等知识。通过该程序,体会出函数的一个很重要的优势,职责明确,RandomFunc( )函数是负责产生随机数,GetAvg( )负责对产生的数据进行计算。

代码语言:javascript
复制
func RandomFunc(numbers [3]int) [3]int{
   //设置种子,只需一次
   rand.Seed(time.Now().UnixNano()) // 以当前系统时间作为种子参数
   for i := 0; i < len(numbers); i++ {
      // 产生随机数
      numbers[i] = rand.Intn(100)// 限制在100内的数
   }
   return numbers
}

func GetAvg(numbers [3]int) float64 {
   var sum int
   for _, v := range numbers {
      sum += v
   }
   return float64(sum) / float64(len(numbers))
}

func main() {
   // 随机生成数组
   var num [3]int
   num = RandomFunc(num)

   // 计算数组的平均值
   avg := GetAvg(num)
   fmt.Printf("%.2f", avg)
}
练习4:用函数实现字符串数组{ "中国", "美国", "巴西", "澳大利亚", "加拿大" }中的内容反转,并在主函数(main( ))中,输出反转的结果。
代码语言:javascript
复制
func Reverse(str [5]string) [5]string {
   var temp string
   for i := 0; i < len(str)/2; i++ {
      temp = str[i]
      str[i] = str[len(str)-1-i]
      str[len(str)-1-i] = temp
   }
   return str
}

func main() {
   counties := [...]string{"中国", "美国", "巴西", "澳大利亚", "加拿大"}
   array := Reverse(counties)
   fmt.Println("反转前的数组: ", counties)
   fmt.Println("反转后的数组: ", array)
}

执行如下:

代码语言:javascript
复制
反转前的数组:  [中国 美国 巴西 澳大利亚 加拿大]
反转后的数组:  [加拿大 澳大利亚 巴西 美国 中国]

注意:该程序中定义的函数,返回的是数组。

练习5:完成两个数组中元素的比较,判断其相同下标对应的元素是否完全一致。例如:a := [5]int{1, 2, 3, 4, 5}与b:= [3]int{1, 2, 3}进行比较,判断其数组元素是否完全一致。

思路:

1:可以先计算出数组a和数组b的长度,如果长度不一致,数组肯定不一致。

2:如果长度一致,可以通过循环将每个数组中元素取出来,进行比较。如果有一个元素不一致就退出循环。

  • 那么上面举例的 a数组与b数组中的元素肯定不一致,因为长度不一致。
  • 如果将数组b修改成b:=[5]int{1,2,3}, 是否一致呢?不一致,虽然长度相等,但是b[3]和b[4]都是0,元素不一致。
  • 如果将数组b修改成b:=[5]int{5,1,2,3,4}是否一致呢?不一致,因为a[0]是1,而b[0]是5.

根据以上分析的思路,代码案例如下:

代码语言:javascript
复制
func main() {
   a := [5]int{1, 2, 3, 4, 5}
   b := [5]int{5, 2, 3, 4, 5}
   if len(a) == len(b) {
      for i := 0; i < len(a); i++ {
         if a[i] == b[i] {
            continue // 如果元素相同,直接进入下一次循环
         } else {
            fmt.Println("两个数组元素不一致")
            break // 元素不一致,跳出循环
         }
      }
   } else {
      fmt.Println("两个数组的元素不一致")
   }
} 

在上面的代码中,重点体会continuebreak的用法。

虽然以上代码能够满足需求,但是问题是比较麻烦,GO语言中提供了另外一种方式进行比较,

直接使用“==”或”!=”进行比较。但是具体的要求是:

1:只支持 ==!=, 比较是不是每一个元素都一样

2:两个数组比较,数组类型要一样

代码语言:javascript
复制
func main() {
   a := [5]int{1, 2, 3, 4, 5}
   b := [5]int{1, 2, 3, 4, 5}
   c := [5]int{1, 2, 3}
   d := [5]int{5, 1, 2, 3, 4}
   f := [3]int{1, 2, 3}
   fmt.Println("a == b", a == b)
   fmt.Println("a == c", a == c)
   fmt.Println("a == d", a == d)
   fmt.Println("a == f", a == f) // 长度不一致,直接异常
}

除了可以进行比较以外,同类型的数组可以赋值

代码语言:javascript
复制
var d [5]int
d = a
fmt.Println("d = ", d)

1.7 二维数组(了解)

前面定义的数组只有一个下标,称之为一维数组,如果有两个下标,称之为二维数组。

关于二维数组,只要了解其基本使用就可以。

二维数组的定义如下:

代码语言:javascript
复制
b := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}
//可以理解数组b有3行4列构成,共能够存储12组数据。

//部分初始化,没有初始化的值为0
c := [3][4]int{{1, 2, 3}, {5, 6, 7, 8}, {9, 10}}
d := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
e := [3][4]int{1: {5, 6, 7, 8}}//对第二列进行初始化,其它采用默认值。

由于二维数组是有行与列构成的,所以通过for循环进行初始化,需要循环嵌套,如下所示:

代码语言:javascript
复制
func main() {
   var a [3][4]int
   k := 0
   for i := 0; i < 3; i++ { // 对行进行循环
      for j := 0; j < 4; j++ { // 对列进行循环
         k++
         a[i][j] = k
         fmt.Printf("a[%d][%d] = %d, ", i, j, a[i][j])
      }
      fmt.Printf("\n")
   }
   fmt.Println("a = ", a)
}

执行如下:

代码语言:javascript
复制
a[0][0] = 1, a[0][1] = 2, a[0][2] = 3, a[0][3] = 4,
a[1][0] = 5, a[1][1] = 6, a[1][2] = 7, a[1][3] = 8,
a[2][0] = 9, a[2][1] = 10, a[2][2] = 11, a[2][3] = 12,
a =  [[1 2 3 4] [5 6 7 8] [9 10 11 12]]

“总结:有多少个[]就是多少维,有多少个[]就用多少个循环 ”

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-07-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 海洋的渔夫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 9. Go复合类型-数组
    • 前言
      • 1:数组
        • 1.1 数组定义
        • 1.2 数组赋值
        • 1.3 数组初始化
        • 1.4 数组练习
        • 1.5 数组冒泡排序
        • 1.6 数组作为函数参数
        • 1.7 二维数组(了解)
    相关产品与服务
    对象存储
    对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档