头图

1 数组

数组是相同数据类型的一组长度固定的序列,类型可以是整形、字符串和浮点型等,数组元素可以通过索引来读取或者修改,索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推,最后一个元素的索引为数组长度减1。

image.png

1.1 数组声明

声明数组必须指定元素类型和数组长度,数组一旦声明,长度就不可变。

语法:

var 数组名 [数组长度] 元素类型

代码示例:

package main

import "fmt"

func main() {
  var intArr [5]int          // 声明一个长度为5的int类型数组
  var floatArr [5]float32        // 声明一个长度为5的float32类型数组
  var strArr [5]string        // 声明一个长度为5的string类型数组
  var boolArr [5]bool          // 声明一个长度为5的bool类型数组
  fmt.Println(intArr)
  fmt.Println(floatArr)
  fmt.Println(strArr)
  fmt.Println(boolArr)
}

由于只是声明了数组,并没有给数组初始化,所以输出均是打印的其类型相对应的默认值。

运行结果:

image.png

1.2 数组初始化

数组初始化时,可以在声明数组时使用大括号{}初始化,或者在数组声明完成之后使用索引对数组中指定位置的元素进行初始化,在指定了数组长度的情况下,初始化中的数组个数不能超过数组长度。

var intArr = [5]int{1, 2, 3, 4, 5}

如果数组长度不确定,可以使用...代替数组长度,编译器在编译时会根据元素个数自动推断数组长度。

var floatArr = [...]float32{1.0, 2.0, 3.0, 4.0, 5.0}

在数组声明之后,可使用索引的方式初始化数组中的元素,索引从0开始,最后一个元素索引为长度减1,不能超过最后一个元素的索引。

var strArr [5]string
strArr[0] = "A"
strArr[1] = "B"
strArr[2] = "C"
strArr[3] = "D"
strArr[4] = "E"

1.3 数组访问和遍历

访问数组中的元素可以使用数组名加中括号[],在中括号中指定元素索引的方式访问,使用索引访问元素时,索引不能超过数组的长度减1。

代码示例:

package main

import "fmt"

func main() {
  var intArr = [5]int{1, 2, 3, 4, 5}
  fmt.Println("第1个元素是:", intArr[0])
  fmt.Println("第2个元素是:", intArr[1])
  fmt.Println("第3个元素是:", intArr[2])
  fmt.Println("第4个元素是:", intArr[3])
  fmt.Println("第5个元素是:", intArr[4])
}

运行结果:

image.png

通过数组索引的方式访问数组方便简洁,但是如果数组长度太长,使用索引的方式访问就不太优雅,这时候就需要使用到数据遍历的功能,数组遍历可以使用for循环的方式进行遍历。

代码示例:

package main

import "fmt"

func main() {
  var intArr = [5]int{1, 2, 3, 4, 5}
  for i := 0; i < len(intArr); i++ {
    fmt.Printf("第%d个元素是: %d \n", i + 1, intArr[i])
  }
}

使用for循环的方式遍历数组intArr,输出结果和上面索引方式一样。

除了使用这种for循环之外,还可以使用for range方式的循环来遍历数组,使用for range遍历更加方便

  • for range使用方式见之前内容。

代码示例:

package main

import "fmt"

func main() {
  var intArr = [5]int{1, 2, 3, 4, 5}
  for i, v := range intArr {
    fmt.Printf("第%d个元素是: %d \n", i + 1, v)
  }
}

使用该方法输出结果同上面一样。

1.4 二维数组

前面所提到的都是一位数组,在go语言中,还有多维数组的概念,但是多维数组中使用到的最多的就是二维数组,所谓二维数组,其本质上可以看做是多个一位数组组成的。

image.png

如上图所示,就是一个简单的二维数组,可以将该二维数组看做是由三个一维数组所组成,就构成了一个表格的形式,访问二维数组需要指定行索引和列索引,且索引都是从0开始,可以使用arr[i][j]来访问,i表示行,j表示列。

二维数组声明和一维数组类似:

var 数组名 [行数][列数] 数组类型

二维数组声明:

var intArr [3][4]int

二维数组声明并初始化:

package main

import "fmt"

func main() {
  intArr := [3][4]int{
    {0, 1, 2, 3} ,     // 第一行
    {4, 5, 6, 7} ,     // 第二行
    {8, 9, 10, 11},    // 第三行
  }
  fmt.Println(intArr)
}

运行结果:

image.png

二维数组可以使用arr[i][j]方式进行访问,i表示行,j表示列,i和j都不能超过索引。

代码示例:

package main

import "fmt"

func main() {
  intArr := [3][3]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
  }
  fmt.Println("第1行第1列:", intArr[0][0])
  fmt.Println("第1行第2列:", intArr[0][1])
  fmt.Println("第1行第3列:", intArr[0][2])
  fmt.Println("第2行第1列:", intArr[1][0])
  fmt.Println("第2行第2列:", intArr[1][1])
  fmt.Println("第2行第3列:", intArr[1][2])
  fmt.Println("第3行第1列:", intArr[2][0])
  fmt.Println("第3行第2列:", intArr[2][1])
  fmt.Println("第3行第3列:", intArr[2][2])
}

运行结果:

image.png

二维数组同样可以使用for循环进行遍历,一维数组使用一个for循环进行遍历,二维数组则使用两个for嵌套循环即可遍历。

代码示例:

package main

import "fmt"

func main() {
  intArr := [3][3]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
  }
  for i := 0; i < len(intArr); i++ {      // len(intArr)获取到的是intArr的行数,intArr[i] 表示一个一维数组
    for j := 0; j < len(intArr[i]); j++ {  // 按照一维数组的方式遍历即可
      fmt.Printf("第%d行第%d列:%d \n", i + 1, j + 1, intArr[i][j])
    }
  }
}

该方法使用两个for循环嵌套,外层循环控制二维数组的行数,len(intArr)获取到的是二维数组的总行数,内层循环控制二维数组的列数,len(intArr[i])获取到的是二维数组当前行的列数,运行结果和上面一样。

使用for range

package main

import "fmt"

func main() {
  intArr := [3][3]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
  }
  for i, v := range intArr {    // 直接循环intArr,获取到的每一个v为一个一维数组
    for i1, v1 := range v {    // 按照一维数组的方式遍历即可
      fmt.Printf("第%d行第%d列:%d \n", i + 1, i1 + 1, v1)
    }
  }
}

运行结果同上。

2 切片

在go语言中,切片是对数组的抽象,数组在声明时指定了长度之后就不可再进行改变,在特定场景下数组就不适用,所以就有了切片类型,切片就是“动态数组”,和数组相比,切片的长度是不固定的,可以在切片后面追加元素,长度自动扩容。

2.1 切片定义

定义切片和定义数组很像,区别就是定义一个切片不需要指定长度。

var 切片名 []类型

切片还可以使用make函数定义。

make([]T, length, capacity)

make函数有三个参数:

  1. 第一个参数为切片类型,可以是[]int,[]string,[]float32等。
  2. 第二个参数为切片初始长度。
  3. 第三个为切片容量,该参数为可选参数。

2.2 切片初始化

一个切片在初始化之前为空切片(nil),长度为0,可以在声明切片时直接初始化切片,如下表示声明一个int切片,初始化值为{1, 2, 3}

s :=[] int {1,2,3 }

初始化为数组的引用,假设有一个数组arr,在初始化为数组的引用时,通过开始索引和结束索引控制初始化的切片大小和切片内元素个数。

s := arr[startIndex:endIndex]  // 从 startIndex 到 endIndex - 1 初始化为一个切片
s := arr[startIndex:]      // 从 startIndex 到 数组结尾 初始化为一个切片
s := arr[:endIndex]        // 从 数组开始 到 endIndex - 1 初始化为一个切片
s := arr[:]            // 从 数组开始 到 数组结尾 初始化为一个切片(整个数组)

代码示例:

package main

import "fmt"

func main() {
  arr := [5]int{1, 2, 3, 4, 5}
  s1 := arr[:]
  s2 := arr[2:]
  s3 := arr[:3]
  s4 := arr[1:4]
  fmt.Println(s1)
  fmt.Println(s2)
  fmt.Println(s3)
  fmt.Println(s4)
}

运行结果:

image.png

2.3 append和copy

append表示在一个切片的末尾追加元素。

copy表示复制一个切片里面的元素到另一个切片。

代码示例:

package main

import "fmt"

func main() {
  s := []int{1,2,3}
  fmt.Println("切片s:",s)
  s = append(s, 4)          // 添加一个元素4到切片s中
  fmt.Println("切片s:",s)
  s1 := make([]int, 4)
  copy(s1, s)                      // 将切片s的内容拷贝到切片s1中
  fmt.Println("切片s1:",s1)
}

运行结果:

image.png

2.4 切片截取

切片截取使用中括号[],通过指定需要截取的开始索引和结束索引。

代码示例:

package main

import "fmt"

func main() {
  s := []int{1,2,3,4,5}
  fmt.Println("完整切片:", s)
  fmt.Println("s[1:3]:", s[1:3])      // 截取索引1(包含)到索引3(不包含)
  fmt.Println("s[:4]:", s[:4])      // 默认开始索引为0
  fmt.Println("s[2:]:", s[2:])      // 默认结束索引为len
}

运行结果:

image.png

2.5 len和cap

长度和容量区别:

  • 长度:长度表示切片中实际存储的元素个数
  • 容量:容量表示切片底层使用的数组的大小

当定义一个切片时,如果没有通过make方法指定cap,则底层会申请一个和切片长度一样的数组,这个数组的大小就是cap,当使用append朝切片中追加元素时,如果追加元素后新的len小于cap,则底层数组不会改变,当新的len大于cap时,底层就会重新申请一个数组,且数组的长度为cap * 2,然后将之前数组的元素全部复制到新数组中。

代码示例:

package main

import "fmt"

func main() {
  s := make([]int, 3)
  fmt.Printf("len:%d, cap:%d \n", len(s), cap(s))    // 此时len=3,cap=3

  s = append(s, 1)
  fmt.Printf("len:%d, cap:%d \n", len(s), cap(s))    // append一个元素,len=4,大于cap,所以底层数组扩容,cap=6

  s = append(s, 1)
  fmt.Printf("len:%d, cap:%d \n", len(s), cap(s))    // append一个元素,len=5,小于cap,底层数组不变,cap=6

  s = append(s, 1)
  fmt.Printf("len:%d, cap:%d \n", len(s), cap(s))    // append一个元素,len=6,小于cap,底层数组不变,cap=6

  s = append(s, 1)
  fmt.Printf("len:%d, cap:%d \n", len(s), cap(s))    // append一个元素,len=7,大于cap,所以底层数组扩容,cap=12
}

运行结果:

image.png

本文参与了SegmentFault 思否写作挑战赛活动,欢迎正在阅读的你也加入。

CodeJR
12 声望0 粉丝