Go 语言基础之数组

数组定义

Go 语言中定义数组的格式如下:

var 数组名称 [数组长度]数组每一项数据的类型

比如要定义一个长度为 3 的整数类型数组:

var arr [3]int

注意在 Go 语言中,数组的类型是由数组长度和数据类型决定的,即 [3]int[5]int 是不同的类型,另外数组长度一经定义后就不可以修改了,数组长度是一个常量。

当数组定义后而没有赋值,则会根据类型而赋默认值,比如整数或浮点数类型默认值是 0,字符串类型默认值是 '',布尔类型默认值是 false 等:

func main() {
        var arr1 [3]int
        var arr2 [3]float64
        var arr3 [3]string
        var arr4 [3]bool

        fmt.Println(arr1) // [0 0 0]
        fmt.Println(arr2) // [0 0 0]
        fmt.Println(arr3) // [     ]
        fmt.Println(arr4) // [false false false]
}

如果要给数组赋值,所有值是用 {}包含,而不是一些语言中使用的 []

func main() {
        var arr1 [3]int
        arr1 = [3]int{1, 2, 3}

        var arr2 [3]string
        arr2 = [3]string{"go", "java", "javascript"}

        fmt.Println(arr1) // [1 2 3]
        fmt.Println(arr2) // [go java javascript]
}

我们注意到,打印出来的数组值还是使用 [] 包含的,但是没有使用 , 隔开。如果是写 JavaScript 的前端,可能会不习惯 Go 语言中这种定义数组的方式,熟悉就好了。

数组可以定义和赋值一起写,即:

func main() {
        arr1 := [3]int{1, 2, 3}

        arr2 := [3]string{"go", "java", "javascript"}

        fmt.Println(arr1) // [1,2,3]
        fmt.Println(arr2) // [go java javascript]
}

这样代码更简洁。

长度未知的数组的定义

有时候我们要定义一个数组时,并不能预测它需要多大的长度,而且 Go 语言数组不可以修改长度,所以此时就需要一种方式能让我们定义可变长度的数组。Go 语言中提供了 ... 操作符来实现这个功能,使用如下:

func main() {
        arr1 := [...]int{1, 2, 3}

        fmt.Println(arr1) // [1 2 3]
        fmt.Printf("%T", arr1) // %T 表示打印值类型,数组 arr1 的类型是 [3]int
}

使用 ... 来替代具体的长度数值,就可以在赋值时自动推断出数组长度,需要注意此时数组的类型中的长度,是根据具体数组值来推断的。

指定数组索引值初始化数组

数组的每一项值,可以使用索引获取,索引值从 0 开始,最大索引是 len-1

func main() {
        arr := [3]int{3, 6, 9}
        fmt.Print(arr[1]) // 6
}

而在数组初始化时,也可以使用索引来指定值,比如:

func main() {
        arr := [...]int{2, 3: 1}
        fmt.Print(arr) // [2 0 0 1]
}

代码中数组第一项值是 2,然后指定索引为 3 的值是 1,此时会自动补全中间两位的值为 0。注意这时候的数组长度不能小于索引,如这样就是错的:

arr := [3]int{2, 3: 1} // error: array index 3 out of bounds

指定数组的长度是 3,但是索引最大也是 3,此时数组应该长度应该是 4,所以报错了。

多维数组

Go 语言支持多维数组,这里以二维数组为例。

定义二维数组例子:

func main() {
        arr := [3][2]string{
                {"北京", "上海"},
                {"杭州", "南京"},
                {"广州", "深圳"},
        }

        fmt.Println(arr) // [[北京 上海] [杭州 南京] [广州 深圳]]
        fmt.Printf("%T", arr) // [3][2]string
        fmt.Println(arr[1][1])  // 南京
}

其实也很好理解,[3][2]string 表示二维数组的长度是 3,数组每一项是类型为 [2]string 的一维数组。注意二维数组赋值时使用的始终是 {},我差点在里面写成 [],这是 js 写多的后果~

有一个注意的地方就是在多维数组中,...只能用在第一层,拿上面例子来说明:

// ... 使用在外层可以自动推断二维数组的长度
arr := [...][2]string{
        {"北京", "上海"},
        {"杭州", "南京"},
        {"广州", "深圳"},
}

// 下面这样 ... 使用在内层是错误的
arr := [3][...]string{
        {"北京", "上海"},
        {"杭州", "南京"},
        {"广州", "深圳"},
}

遍历数组

Go 语言遍历数组,主要有两种方法,一个是用 for 循环遍历,另一个是用 range 遍历,这里简单写几个例子。

使用 for 循环遍历:

// 遍历一维数组
func main() {
        arr := [3]string{"Golang", "Java", "JavaScript"}

        for i := 0; i < len(arr); i++ {
                fmt.Println(arr[i])
        }
}

// 输出
Golang
Java
JavaScript


// 遍历二维数组
func main() {
        arr := [2][3]string{
                {"Golang", "Java", "JavaScript"},
                {"Python", "PHP", "C"},
        }

        for i := 0; i < len(arr); i++ {
                fmt.Println(arr[i])
        }
}

// 输出
[Golang Java JavaScript]
[Python PHP C]

使用 range 遍历:

// 遍历一维数组
func main() {
        arr := [3]string{"Golang", "Java", "JavaScript"}

        for idx, val := range arr {
                fmt.Println(idx, val)
        }
}

// 输出索引和值
0 Golang
1 Java
2 JavaScript


// 遍历二维数组
func main() {
        arr := [2][3]string{
                {"Golang", "Java", "JavaScript"},
                {"Python", "PHP", "C"},
        }

        for _, val := range arr {  // 当我们要忽略索引值时,可以使用 _
                fmt.Println(val)
        }
}

// 输出
[Golang Java JavaScript]
[Python PHP C]

数组是值类型

不得不说一嘴,Go 语言中的数组是值类型,这跟 js 这种动态语言中的数组很不一样,在接触 Go 语言之前,我一直坚信数组就是引用类型,但现在要习惯不同编程语言的特性。

值类型和引用类型的代码区别就是,当修改复制出来的数据时,会不会改变原数据,如下代码证明了 Go 中的数组是值类型:

func main() {
        nums := [3]int{3, 5, 7}

        copy_nums := nums  // 复制数组

        copy_nums[0] = 4  // 修改复制数组的值

        fmt.Println(nums) // [3 5 7]
        fmt.Println(copy_nums) // [4 5 7]
}

如果数组是引用类型,那么原数组 nums 的值也应该是 [4 5 7],但是它还保持原值,所以它是值类型。