GOlang数组的详解

[复制链接]
查看2128 | 回复1 | 2019-6-10 10:00:38 | 显示全部楼层 |阅读模式
数组的四种声明方法
  1. //第一种
  2. //var <数组名称> [<数组长度>]<数组元素>
  3. var arr [2]int
  4.     arr[0]=1
  5.     arr[1]=2

  6. //第二种
  7. //var <数组名称> = [<数组长度>]<数组元素>{元素1,元素2,...}
  8. var arr = [2]int{1,2}
  9. //或者
  10. arr := [2]int{1,2}

  11. //第三种
  12. //var <数组名称> [<数组长度>]<数组元素> = [...]<元素类型>{元素1,元素2,...}
  13. var arr = [...]int{1,2}
  14. //或者
  15. arr := [...]int{1,2}

  16. //第四种
  17. //var <数组名称> [<数组长度>]<数组元素> = [...]<元素类型>{索引1:元素1,索引2:元素2,...}
  18. var arr = [...]int{1:1,0:2}
  19. //或者
  20. arr := [...]int{1:1,0:2}
复制代码
1 数组介绍
数组是同一类型元素的集合。例如,整数集合 5,8,9,79,76 形成一个数组。Go 语言中不允许混合不同类型的元素,例如包含字符串和整数的数组。(注:当然,如果是 interface{} 类型数组,可以包含任意类型) 。
2 数组常见操作
一个数组的表示形式为 [n]T。n 表示数组中元素的数量,T 代表每个元素的类型。元素的数量 n 也是该类型的一部分 。
2.1 数组初始化
一维数组初始化如下
  1. func main() {
  2.     var a [4]int    //元素自动初始化为零[0 0 0 0]
  3.     b := [4]int{2, 5}  //未提供初始化值得元素自动初始化为0  [2 5 0 0]
  4.     c := [4]int{5, 3: 10} //可指定索引位置初始化 [5 0 0 10]
  5.     d := [...]int{1, 2, 3} //编译器按初始化值数量确定数组长度 [1 2 3]
  6.     e := [...]int{10, 3: 100} //支持索引初始化,但注意数组长度与此有关 [10 0 0 100]
  7.     fmt.Println(a, b, c, d, e)
  8. }
复制代码
对于结构等复合类型,可省略元素初始化类型标签
  1. package main

  2. import "fmt"

  3. func main() {
  4.     type user struct {
  5.         name string
  6.         age  byte
  7.     }

  8.     d := [...]user{
  9.         {"tom", 20},// 可省略元素类型。
  10.         {"lee", 18},// 别忘了最后一行的逗号。
  11.     }

  12.     fmt.Printf("%#v\n", d)
  13. }
  14. /*output
  15. [2]main.user{main.user{name:"tom", age:0x14}, main.user{name:"lee", age:0x12}}
  16. */
复制代码
在定义多维数组时,仅第一维度允许使用“…”
  1. package main

  2. import "fmt"

  3. func main() {
  4.     a := [2][2]int{
  5.         {1, 2},
  6.         {3, 4},
  7.     }

  8.     b := [...][2]int{
  9.         {10, 20},
  10.         {30, 40},
  11.     }

  12.     c := [...][2][2]int{   //三维数组
  13.         {
  14.             {1, 2},
  15.             {3, 4},
  16.         },
  17.         {
  18.             {10, 20},
  19.             {30, 40},
  20.         },
  21.     }

  22.     fmt.Println(a)  //[[1 2] [3 4]]
  23.     fmt.Println(b)  //[[10 20] [30 40]]
  24.     fmt.Println(c)  //[[[1 2] [3 4]] [[10 20] [30 40]]]
  25. }
复制代码
多维数组定义
  1. package main

  2. import (
  3.     "fmt"
  4. )

  5. var arr0 [5][3]int
  6. var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}

  7. func main() {
  8.     a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
  9.     b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。
  10.     fmt.Println(arr0, arr1)
  11.     fmt.Println(a, b)
  12. }

  13. /*
  14. output
  15. [[0 0 0] [0 0 0] [0 0 0] [0 0 0] [0 0 0]] [[1 2 3] [7 8 9]]
  16. [[1 2 3] [4 5 6]] [[1 1] [2 2] [3 3]]
  17. */
复制代码
2.2 数组索引
数组的索引从 0 开始到 length - 1 结束
  1. func main() {
  2.     var a [3]int //int array with length 3
  3.     a[0] = 12    // array index starts at 0
  4.     a[1] = 78
  5.     a[2] = 50
  6.     fmt.Println(a)
  7. }
复制代码
2.3 数组是值类型
Go 中的数组是值类型而不是引用类型。这意味着当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。如果对新变量进行更改,则不会影响原始数组。
  1. func main() {
  2.     a := [...]string{"USA", "China", "India", "Germany", "France"}
  3.     b := a // a copy of a is assigned to b
  4.     b[0] = "Singapore"
  5.     fmt.Println("a is ", a)  //a is  [USA China India Germany France]
  6.     fmt.Println("b is ", b)  //b is  [Singapore China India Germany France]
  7. }
复制代码
上述程序中,a 的副本被赋给 b。在第 4 行中,b 的第一个元素改为 Singapore。这不会在原始数组 a 中反映出来。
同样,当数组作为参数传递给函数时,它们是按值传递,而原始数组保持不变。
  1. package main

  2. import "fmt"

  3. func changeLocal(num [5]int) {
  4.     num[0] = 55
  5.     fmt.Println("inside function ", num)
  6. }

  7. func main() {
  8.     num := [...]int{5, 6, 7, 8, 8}
  9.     fmt.Println("before passing to function ", num)
  10.     changeLocal(num) //num is passed by value
  11.     fmt.Println("after passing to function ", num)
  12. }

  13. /*output
  14. before passing to function  [5 6 7 8 8]
  15. inside function  [55 6 7 8 8]
  16. after passing to function  [5 6 7 8 8]
  17. */
复制代码
在上述程序的 172 行中, 数组 num 实际上是通过值传递给函数 changeLocal,数组不会因为函数调用而改变。
值拷贝行为会造成性能问题,通常会建议使用 slice,或数组指针。
  1. package main

  2. import (
  3.     "fmt"
  4. )

  5. func test(x [2]int) {
  6.     fmt.Printf("x: %p\n", &x)
  7.     x[1] = 1000
  8. }
  9. func main() {
  10.     a := [2]int{}
  11.     fmt.Printf("a: %p\n", &a)
  12.     test(a)
  13.     fmt.Println(a)
  14. }
  15. /*
  16. output:
  17. a: 0xc042062080
  18. x: 0xc0420620c0
  19. [0 0]
  20. */
复制代码
2.4 数组长度和元素数量
通过将数组作为参数传递给 len 函数,可以得到数组的长度。 cap可以得到元素数量
  1. package main

  2. import "fmt"

  3. func main() {
  4.     a := [...]float64{67.7, 89.8, 21, 78}
  5.     fmt.Println("length of a is", len(a)) //length of a is 4
  6.     fmt.Println("num of a is",cap(a)) //num of a is 4
  7. }
复制代码
  • 注意:内置函数len和cap都返回第一维度长度
  1. package main

  2. func main() {
  3.     a := [2]int{}
  4.     b := [...][2]int{
  5.         {10, 20},
  6.         {30, 40},
  7.         {50, 60},
  8.     }

  9.     println(len(a), cap(a))   // 2 2
  10.     println(len(b), cap(b))   // 3 3
  11.     println(len(b[1]), cap(b[1]))  // 2 2
  12. }
复制代码
2.5 使用 range 迭代数组
for 循环可用于遍历数组中的元素。
  1. package main

  2. import "fmt"

  3. func main() {
  4.     a := [...]float64{67.7, 89.8, 21, 78}
  5.     for i := 0; i < len(a); i++ {
  6.         // looping from 0 to the length of the array
  7.         fmt.Printf("%d th element of a is %.2f\n", i, a[i])
  8.     }
  9. }
复制代码
上面的程序使用 for 循环遍历数组中的元素,从索引 0 到 length of the array - 1
Go 提供了一种更好、更简洁的方法,通过使用 for 循环的 range 方法来遍历数组。range返回索引和该索引处的值。让我们使用 range 重写上面的代码。我们还可以获取数组中所有元素的总和。
  1. package main

  2. import "fmt"

  3. func main() {
  4.     a := [...]float64{67.7, 89.8, 21, 78}
  5.     sum := float64(0)
  6.     for i, v := range a {
  7.         //range returns both the index and value
  8.         fmt.Printf("%d the element of a is %.2f\n", i, v)
  9.         sum += v
  10.     }
  11.     fmt.Println("\nsum of all elements of a", sum)
  12. }
复制代码
上述程序的第 270 行 for i, v := range a 利用的是 for 循环 range 方式。 它将返回索引和该索引处的值。 我们打印这些值,并计算数组 a 中所有元素的总和。
如果你只需要值并希望忽略索引,则可以通过用 _ 空白标识符替换索引来执行。
  1. for _, v := range a { // ignores index  }
复制代码
上面的 for 循环忽略索引,同样值也可以被忽略。
2.6 数组操作符操作
如元素类型支持”==,!=”操作符,那么数组也支持此操作
  1. package main

  2. func main() {
  3.     var a, b [2]int
  4.     println(a == b)  //true

  5.     c := [2]int{1, 2}
  6.     d := [2]int{0, 1}
  7.     println(c != d) //true
  8.     /*
  9.         var e, f [2]map[string]int
  10.         println(e == f)  //invalid operation: e == f ([2]map[string]int cannot be compared)
  11.     */
  12. }
复制代码
3 数组高级用法3.1 多维数组
到目前为止我们创建的数组都是一维的,Go 语言可以创建多维数组。
  1. package main

  2. import (
  3.     "fmt"
  4. )

  5. func printArray(a [3][2]string) {
  6.     for _, v1 := range a {
  7.         for _, v2 := range v1 {
  8.             fmt.Printf("%s ", v2)
  9.         }
  10.         fmt.Printf("\n")
  11.     }
  12. }

  13. func main() {
  14.     a := [3][2]string{
  15.         {"lion", "tiger"},
  16.         {"cat", "dog"},
  17.         {"pigeon", "peacock"}, // this comma is necessary. The compiler will complain if you omit this comma
  18.     }
  19.     printArray(a)
  20.     var b [3][2]string
  21.     b[0][0] = "apple"
  22.     b[0][1] = "samsung"
  23.     b[1][0] = "microsoft"
  24.     b[1][1] = "google"
  25.     b[2][0] = "AT&T"
  26.     b[2][1] = "T-Mobile"
  27.     fmt.Printf("\n")
  28.     printArray(b)
  29. }

  30. /*output
  31. lion tiger
  32. cat dog
  33. pigeon peacock

  34. apple samsung
  35. microsoft google
  36. AT&T T-Mobile
  37. */
复制代码
多维数组遍历
  1. package main

  2. import (
  3.     "fmt"
  4. )

  5. func main() {
  6.     var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
  7.     for k1, v1 := range f {
  8.         for k2, v2 := range v1 {
  9.             fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
  10.         }
  11.         fmt.Println()
  12.     }
  13. }
  14. /*
  15. output:
  16. (0,0)=1 (0,1)=2 (0,2)=3
  17. (1,0)=7 (1,1)=8 (1,2)=9
  18. */
复制代码
3.2 数组指针和指针数组
要分清指针数组和数组指针的区别。指针数组是指元素为指针类型的数组,数组指针是获取数组变量的地址。
  1. package main

  2. import "fmt"

  3. func main() {
  4.     x, y := 10, 20
  5.     a := [...]*int{&x, &y}
  6.     p := &a

  7.     fmt.Printf("%T,%v\n", a, a)  //[2]*int,[0xc042062080 0xc042062088]
  8.     fmt.Printf("%T,%v\n", p, p)  //*[2]*int,&[0xc042062080 0xc042062088]
  9. }
复制代码
可获取任意元素地址
  1. func main() {
  2.     a := [...]int{1, 2}
  3.     println(&a, &a[0], &a[1])  //0xc042049f68 0xc042049f68 0xc042049f70
  4. }
复制代码
数组指针可以直接用来操作元素
  1. func main() {
  2.     a := [...]int{1, 2}
  3.     p := &a

  4.     p[1] += 10
  5.     println(p[1])   //12
  6. }
复制代码
4 数组使用常见坑
定义数组类型时,数组长度必须是非负整型常量表达式,长度是类型组成部分。也就是说,元素类型相同,但长度不同的数组不属于同一类型。
例子:
  1. func main() {
  2.     var d1 [3]int
  3.     var d2 [2]int
  4.     d1 = d2 //cannot use d2 (type [2]int) as type [3]int in assignment
  5. }
复制代码
5 数组总结
  • 数组:是同一种数据类型的固定长度的序列。
  • 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
  • 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
  • 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1。数组索引常用操作如下:

  1. for i := 0; i < len(a); i++ {
  2.    ...
  3. }

  4. for index, v := range a {
  5.    ...
  6. }
复制代码
  • 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
  • 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
  • 支持 “==”、”!=” 操作符,因为内存总是被初始化过的。
  • 指针数组 [n]T,数组指针[n]T。

回复

使用道具 举报

fengzi | 2019-9-14 14:51:50 | 显示全部楼层
好好
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则