Go语言快速入门指南二:数组、切片、map、函数和面向对象 | 豆包MarsCode AI 刷题

40 阅读8分钟

数组

Go语言中的数组是固定长度的数据结构,其中的元素类型必须相同。数组的长度是数组类型的一部分,意味着不同长度的数组是不同类型。

Go语言的数组有一些内置的性能优势,但它们的大小必须在编译时确定,不能动态变化。

下面是一个数组的例子:

package main

import "fmt"

func main() {
    // 1. 声明一个数组并初始化
    var arr [5]int  // 声明一个包含5个整数的数组,元素默认值为 0
    arr[0] = 10     // 给元素赋值
    arr[1] = 20     
    arr[2] = 30     
    arr[3] = 40     
    arr[4] = 50     

    fmt.Println("Array:", arr) // 输出数组

    // 2. 声明并初始化数组
    arr2 := [3]string{"apple", "banana", "cherry"} // 声明一个包含3个字符串的数组并初始化
    fmt.Println("Array2:", arr2) // 输出: [apple banana cherry]

    // 3. 获取数组的长度
    fmt.Println("Length of arr:", len(arr)) // 输出: 5
    fmt.Println("Length of arr2:", len(arr2)) // 输出: 3

    // 4. 遍历数组
    fmt.Println("Iterating over arr:")
    for i, v := range arr {
        fmt.Printf("Index: %d, Value: %d\n", i, v)
    }

    fmt.Println("Iterating over arr2:")
    for i, v := range arr2 {
        fmt.Printf("Index: %d, Value: %s\n", i, v)
    }

    // 5. 多维数组
    arr3 := [2][3]int{{1, 2, 3}, {4, 5, 6}} // 声明一个 2x3 的二维数组
    fmt.Println("2D Array arr3:", arr3)
    // 遍历二维数组
    for i := 0; i < len(arr3); i++ {
        for j := 0; j < len(arr3[i]); j++ {
            fmt.Printf("arr3[%d][%d] = %d\n", i, j, arr3[i][j])
        }
    }
}

var arr [5]int 声明了一个包含 5 个整数的数组,数组的默认值为零值(对于整数类型是 0)。

arr2 := [3]string{"apple", "banana", "cherry"} 声明并初始化一个包含 3 个字符串元素的数组。

len() 函数可以返回数组的长度。

使用 for 循环和 range 关键字可以遍历数组元素。range 返回数组的索引和值。

arr3 := [2][3]int{{1, 2, 3}, {4, 5, 6}} 声明了一个 2 行 3 列的二维数组。可以通过嵌套的 for 循环遍历每一个元素。

数组在Go中是固定大小的,不能改变大小;数组有一些自带的方法,如使用len()可以获取数组的长度;Go也支持多维数组,可以用于存储矩阵等结构。

切片

Go 语言中的切片(slice)是一种底层数组的封装,提供了动态长度数组的功能,是 Go 中更常用的集合类型。下面是一个切片的示例。

package main

import "fmt"

func main() {
    // 1. 创建一个切片
    slice1 := []int{1, 2, 3, 4, 5} // 使用字面量创建一个切片
    fmt.Println("slice1:", slice1)

    // 2. 切片的长度和容量
    fmt.Println("Length of slice1:", len(slice1))  // 输出切片的长度
    fmt.Println("Capacity of slice1:", cap(slice1)) // 输出切片的容量

    // 3. 使用 make 函数创建切片
    slice2 := make([]int, 5)  // 创建一个长度为5的切片,所有元素的初始值为零
    fmt.Println("slice2:", slice2)
    fmt.Println("Length of slice2:", len(slice2))
    fmt.Println("Capacity of slice2:", cap(slice2))

    // 4. 修改切片中的元素
    slice1[2] = 100  // 修改切片中的第3个元素
    fmt.Println("Modified slice1:", slice1)

    // 5. 切片的扩展 (切片可以通过 append 函数扩展)
    slice1 = append(slice1, 6, 7, 8) // 将 6, 7, 8 添加到 slice1 的末尾
    fmt.Println("slice1 after append:", slice1)

    // 6. 切片的截取
    slice3 := slice1[1:4]  // 从索引 1 到索引 3(不包括索引 4)截取切片
    fmt.Println("slice3 (sliced from slice1):", slice3)

    // 7. 遍历切片
    fmt.Println("Iterating over slice1:")
    for i, v := range slice1 {
        fmt.Printf("Index: %d, Value: %d\n", i, v)
    }

    // 8. 多维切片
    matrix := [][]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }
    fmt.Println("Matrix (2D slice):", matrix)
    // 遍历二维切片
    for i, row := range matrix {
        for j, value := range row {
            fmt.Printf("matrix[%d][%d] = %d\n", i, j, value)
        }
    }
}

slice1 := []int{1, 2, 3, 4, 5}:使用字面量创建一个包含 5 个整数的切片。

slice2 := make([]int, 5):使用 make()函数创建一个长度为 5 的切片,所有元素默认值为 0。

len(slice)方法可以获取切片的长度,即当前包含的元素个数。

cap(slice)方法可以获取切片的容量,即底层数组的大小。

索引可以用来定位元素(像C语言一样),比如 slice1[2] = 100 将 slice1 中的第 3 个元素修改为 100。

append(slice, values...) 可以向切片添加元素,切片的长度会自动增加。

使用slice[start:end] 可以从切片中截取一部分。这里 slice1[1:4] 表示从索引 1 到索引 3(不包括索引 4)。

range关键字可以用来遍历切片的索引和值。

二维数组需要用类似matrix := [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} 的语法来创建(有些像Java语法),另外也可以用嵌套的for循环遍历每个元素。

切片在Go中非常灵活,可以动态地增减元素,使用方便。可以使用make创建一个指定长度的切片,也可以直接通过字面量来初始化切片。切片具有动态大小,可以通过append函数扩展。另外切片可以通过截取操作来创建子切片,支持多维切片。

map

在 Go 语言中,map 是一种用于存储键值对的数据结构。它类似于其他编程语言中的字典或哈希表。map 可以快速查找、插入和删除元素。下面是一个 Go 语言中 map 使用的示例:

package main

import "fmt"

func main() {
    // 1. 创建一个 map
    studentScores := make(map[string]int)

    // 2. 向 map 中添加元素
    studentScores["Alice"] = 90
    studentScores["Bob"] = 80
    studentScores["Charlie"] = 95

    // 3. 输出整个 map
    fmt.Println("Student Scores:", studentScores)

    // 4. 查找 map 中的值
    score, exists := studentScores["Bob"]
    if exists {
        fmt.Println("Bob's score:", score)
    } else {
        fmt.Println("Bob's score not found.")
    }

    // 5. 修改 map 中的值
    studentScores["Alice"] = 92
    fmt.Println("Updated Student Scores:", studentScores)

    // 6. 删除 map 中的元素
    delete(studentScores, "Charlie")
    fmt.Println("Student Scores after deletion:", studentScores)

    // 7. 遍历 map
    fmt.Println("Iterating over Student Scores:")
    for name, score := range studentScores {
        fmt.Printf("%s: %d\n", name, score)
    }

    // 8. 判断 map 是否为空
    if len(studentScores) == 0 {
        fmt.Println("The map is empty.")
    } else {
        fmt.Println("The map is not empty.")
    }
}

使用 make(map[string]int) 创建一个空的 map,其中 string 表示键的类型,int 表示值的类型。

studentScores["Alice"] = 90 通过键 "Alice" 添加元素,将其分数设置为 90。

可以使用score, exists := studentScores["Bob"]这样的语法来查找数据,如果键存在,exists 将为 true,否则为 false。

map支持通过K-V键值对方式访问并直接赋值修改 map 中已有的元素,上面studentScores["Alice"] = 92 修改了 Alice 的分数。

使用 delete(studentScores, "Charlie") 删除键 "Charlie" 对应的元素。

使用 range 循环来遍历 map 中的所有键值对。在每一次循环中,name 是键,score 是值。

通过 len(studentScores) 可以检查 map 的长度,如果为 0,则说明 map 是空的。

另外,Go语言是没有自带的Set集合的。虽然可以用map来模拟set,但是会有一些内存浪费,这里可以自己用结构体实现一个。

函数

go的函数和其它不太一样,返回值是放在最后面的,而参数类型也是放在参数名后面。这样说有些抽象,直接看代码:

func add(a int, b int) int {
    return a + b
}

这里a int 表示一个类型为int的变量a;最后的int表示函数的返回值。

另外go也支持返回多个值。此外如果入参都是一样的类型,可以只在最后标注一个类型。

func swap(a, b int) (int, int) {
    return b, a  // 返回两个整数,顺序颠倒
}

Go 允许定义匿名函数(没有名字的函数),并且可以在定义时立即执行。这里和Js类似,函数的参数就是函数体花括号后面圆括号内的值。

package main

import "fmt"

func main() {
    // 定义并立即执行匿名函数
    result := func(a, b int) int {
        return a + b
    }(3, 5)

    fmt.Println("Sum from anonymous function:", result)
}

最后,作为支持函数式编程的语言,Go也支持将函数作为参数传递。

// 定义一个接受函数作为参数的函数
func applyOperation(a, b int, operation func(int, int) int) int {
    return operation(a, b)
}

func main() {
    // 定义加法操作
    add := func(a, b int) int {
        return a + b
    }

    result := applyOperation(5, 3, add)  // 将匿名函数传递给 applyOperation
    fmt.Println("Result of addition:", result)
}

Go也支持函数作为返回值,用来实现闭包(closure)等功能,这里不多赘述了。

另外,上面提到过Go语言也有指针类型,主要就是用来让函数修改调用方变量用的。这个功能在Java中被废除了,在Go中又被加入了回来。

面向对象

结构体

结构体作为面向对象系统的基石,在Go语言中当然也是存在的。

// 定义一个结构体
type Person struct {
    Name string
    Age  int
}

func main() {
    // 创建一个结构体实例
    p := Person{
        Name: "John",
        Age:  30,
    }

    // 访问结构体字段
    fmt.Println("Name:", p.Name)
    fmt.Println("Age:", p.Age)
}

这里出现了指针的第二个用途,可以用指针进行引用传递而非值拷贝。

截至目前这一步都还只是将结构体作为变量使用而已,接下来要引入面向对象:在结构体里引入方法。

Go的类方法不太一样,不是直接写在类里的,而是在函数func关键字后函数名前加上结构体名字。

type Rectangle struct {
    Width  float64
    Height float64
}

// 定义一个方法,计算矩形的面积
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 定义一个方法,修改矩形的宽度
func (r *Rectangle) SetWidth(w float64) {
    r.Width = w
}

func main() {
    // 创建一个结构体实例
    rect := Rectangle{Width: 10, Height: 5}

    // 调用方法
    fmt.Println("Area of rectangle:", rect.Area())

    // 修改矩形的宽度
    rect.SetWidth(15)
    fmt.Println("Updated area of rectangle:", rect.Area())
}

在使用时还是一样的,使用rect.SetWidth()这样的消息传递方法来调用方法。