Go的数组,slice切片,map的使用

在Go语言中,数组是一种固定长度的数据结构,可以存储同一类型的元素。数组的长度是数组类型的一部分,因此在定义时必须明确指定长度。数组的元素可以通过索引访问,索引从0开始。

1. 数组的声明与初始化

在Go中,可以使用多种方式声明和初始化数组。

示例 1:声明数组
go 复制代码
package main

import "fmt"

func main() {
    // 声明长度为5的int数组,默认值为0
    var arr [5]int
    fmt.Println("Array:", arr)
}
示例 2:初始化数组
go 复制代码
package main

import "fmt"

func main() {
    // 声明并初始化数组
    arr := [5]int{1, 2, 3, 4, 5}
    fmt.Println("Initialized Array:", arr)

    // 让Go根据元素数量自动推断数组长度
    autoArr := [...]int{10, 20, 30}
    fmt.Println("Auto-length Array:", autoArr)
}

2. 访问和修改数组元素

数组元素可以通过索引访问和修改。索引从0开始,到len(arr) - 1结束。

示例
go 复制代码
package main

import "fmt"

func main() {
    arr := [5]int{1, 2, 3, 4, 5}

    // 访问元素
    fmt.Println("Element at index 2:", arr[2])

    // 修改元素
    arr[2] = 10
    fmt.Println("Modified Array:", arr)
}

3. 数组的长度和容量

使用内置函数len()可以获取数组的长度。数组的容量固定,无法在运行时改变。

go 复制代码
package main

import "fmt"

func main() {
    arr := [5]int{1, 2, 3, 4, 5}
    fmt.Println("Length of the array:", len(arr))
}

4. 遍历数组

Go支持for循环来遍历数组,可以使用索引或range关键字。

使用索引遍历
go 复制代码
package main

import "fmt"

func main() {
    arr := [3]string{"Go", "Python", "Java"}

    for i := 0; i < len(arr); i++ {
        fmt.Printf("Element at index %d: %s\n", i, arr[i])
    }
}
使用range遍历
go 复制代码
package main

import "fmt"

func main() {
    arr := [3]string{"Go", "Python", "Java"}

    for i, v := range arr {
        fmt.Printf("Index %d: %s\n", i, v)
    }

    // 仅遍历值
    for _, v := range arr {
        fmt.Println("Value:", v)
    }
}

5. 多维数组

Go支持多维数组,比如二维数组可以用于表示矩阵或表格。

示例:二维数组
go 复制代码
package main

import "fmt"

func main() {
    // 声明并初始化一个2x3的二维数组
    var matrix [2][3]int = [2][3]int{
        {1, 2, 3},
        {4, 5, 6},
    }

    // 访问元素
    fmt.Println("Element at [1][2]:", matrix[1][2])

    // 遍历二维数组
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            fmt.Printf("Element at [%d][%d]: %d\n", i, j, matrix[i][j])
        }
    }
}

6. 数组作为函数参数

在Go中,数组作为参数传递时是值传递,即传递数组的副本。因此,在函数内修改数组不会影响原数组。

示例
go 复制代码
package main

import "fmt"

// 函数接收数组参数并修改它
func modifyArray(arr [3]int) {
    arr[0] = 99
    fmt.Println("Inside function:", arr)
}

func main() {
    originalArray := [3]int{1, 2, 3}
    modifyArray(originalArray)
    fmt.Println("Outside function:", originalArray) // 不变,仍为{1, 2, 3}
}

7. 数组和切片的区别

  • 数组:长度固定,存储在栈上,传递是值传递。
  • 切片:动态长度,更灵活,存储在堆上,传递是引用传递。

8. 数组复制

在Go中,直接赋值一个数组会复制整个数组的数据。

go 复制代码
package main

import "fmt"

func main() {
    arr1 := [3]int{1, 2, 3}
    arr2 := arr1 // arr2 是 arr1 的副本

    arr2[0] = 99
    fmt.Println("arr1:", arr1) // 不变,仍为{1, 2, 3}
    fmt.Println("arr2:", arr2) // {99, 2, 3}
}
  • 声明和初始化 :数组的长度固定,类型为[n]T
  • 访问和修改:通过索引操作。
  • 遍历 :可以使用forfor range循环。
  • 多维数组:支持嵌套数组,用于复杂数据结构。
  • 函数参数:数组是值传递,不会修改原数组。
  • 复制:数组赋值会创建新副本。

在Go语言中,slice(切片)是一种非常灵活和强大的数据结构,类似于动态数组。与数组不同,切片的长度是可变的,因此它们可以在程序运行时增长或缩减。

1. 声明和初始化切片

Go语言中,可以使用多种方式声明和初始化切片。

示例 1:使用字面量声明和初始化切片
go 复制代码
package main

import "fmt"

func main() {
    // 声明并初始化切片
    slice := []int{1, 2, 3, 4, 5}
    fmt.Println("Slice:", slice)
}
示例 2:使用make函数创建切片

make函数用于创建切片,可以指定长度和容量。

go 复制代码
package main

import "fmt"

func main() {
    // 创建长度为3,容量为5的切片
    slice := make([]int, 3, 5)
    fmt.Println("Slice:", slice)
    fmt.Println("Length:", len(slice))
    fmt.Println("Capacity:", cap(slice))
}
示例 3:从数组或切片生成切片
go 复制代码
package main

import "fmt"

func main() {
    arr := [5]int{10, 20, 30, 40, 50}
    
    // 从数组创建切片
    slice := arr[1:4] // 包含索引1到3的元素
    fmt.Println("Slice from array:", slice)
}

2. 访问和修改切片元素

切片元素可以通过索引访问和修改,与数组类似。

go 复制代码
package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5}

    // 访问元素
    fmt.Println("Element at index 2:", slice[2])

    // 修改元素
    slice[2] = 10
    fmt.Println("Modified Slice:", slice)
}

3. 切片的长度和容量

使用len()函数获取切片的长度,使用cap()函数获取切片的容量。

go 复制代码
package main

import "fmt"

func main() {
    slice := make([]int, 3, 5)
    fmt.Println("Length of slice:", len(slice))
    fmt.Println("Capacity of slice:", cap(slice))
}

4. 切片的追加

切片可以使用append()函数追加元素。如果切片容量不足,append()会分配新的底层数组。

go 复制代码
package main

import "fmt"

func main() {
    slice := []int{1, 2, 3}
    slice = append(slice, 4, 5)
    fmt.Println("Appended Slice:", slice)
}
扩展示例:追加另一个切片
go 复制代码
package main

import "fmt"

func main() {
    slice1 := []int{1, 2, 3}
    slice2 := []int{4, 5, 6}
    slice1 = append(slice1, slice2...) // 使用 `...` 解包
    fmt.Println("Combined Slice:", slice1)
}

5. 切片的拷贝

使用copy()函数可以将一个切片的内容拷贝到另一个切片中。copy()返回拷贝的元素个数。

go 复制代码
package main

import "fmt"

func main() {
    src := []int{1, 2, 3, 4, 5}
    dest := make([]int, len(src))

    numCopied := copy(dest, src)
    fmt.Println("Number of elements copied:", numCopied)
    fmt.Println("Destination Slice:", dest)
}

6. 切片的底层数组共享

切片实际上是对底层数组的引用,因此多个切片可以共享同一个底层数组

go 复制代码
package main

import "fmt"

func main() {
    arr := [5]int{10, 20, 30, 40, 50}
    slice1 := arr[1:4]
    slice2 := arr[2:5]

    fmt.Println("Slice1:", slice1)
    fmt.Println("Slice2:", slice2)

    // 修改底层数组,影响到所有引用它的切片
    arr[2] = 100
    fmt.Println("Modified Slice1:", slice1)
    fmt.Println("Modified Slice2:", slice2)
}

7. 切片的扩容机制

当切片的容量不足以容纳新元素时,append()会创建一个新的底层数组,其容量通常是现有容量的2倍。

go 复制代码
package main

import "fmt"

func main() {
    slice := make([]int, 2, 3)
    fmt.Println("Initial slice:", slice, "Length:", len(slice), "Capacity:", cap(slice))

    slice = append(slice, 1, 2)
    fmt.Println("Slice after append:", slice, "Length:", len(slice), "Capacity:", cap(slice))
}

这里创建了一个切片 slice,它的类型是 []int(整数切片),初始长度为2,容量为3。这意味着它会初始化一个包含两个零值(即0)的切片,同时预留了足够的空间来存储总共三个整数,而不需要立即分配新的内存。

8. 切片与多维切片

Go语言支持多维切片,但需要手动初始化每个内部切片。

go 复制代码
package main

import "fmt"

func main() {
    // 声明一个二维切片
    matrix := [][]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }

    for i := 0; i < len(matrix); i++ {
        fmt.Println("Row", i, ":", matrix[i])
    }
}
  • 切片是动态数组,长度可以变化。
  • make()和字面量可以用来创建切片。
  • **append()copy()**是切片操作的关键函数。
  • 共享底层数组:修改切片可能会影响到同一底层数组的其他切片。
  • 扩容机制 :切片自动扩容,通常将容量增大到现有的2倍

在Go语言中,map是一种内置的数据结构,用于存储键值对。map的键是唯一的,每个键都对应一个值。map非常适合用于快速查找、添加和删除数据。

1. 声明和初始化map

可以使用make()函数或者字面量语法来声明和初始化map

示例 1:使用make()函数
go 复制代码
package main

import "fmt"

func main() {
    // 声明一个map,键是string类型,值是int类型
    myMap := make(map[string]int)

    // 添加键值对
    myMap["apple"] = 5
    myMap["banana"] = 3

    fmt.Println("Map:", myMap)
}
示例 2:使用字面量初始化
go 复制代码
package main

import "fmt"

func main() {
    // 使用字面量初始化一个map
    myMap := map[string]int{
        "apple":  5,
        "banana": 3,
        "orange": 7,
    }

    fmt.Println("Map:", myMap)
}

2. 访问和修改map元素

可以使用键访问和修改map中的值。

go 复制代码
package main

import "fmt"

func main() {
    myMap := map[string]int{
        "apple":  5,
        "banana": 3,
    }

    // 访问元素
    fmt.Println("Value for key 'apple':", myMap["apple"])

    // 修改元素
    myMap["apple"] = 10
    fmt.Println("Modified value for key 'apple':", myMap["apple"])

    // 添加新元素
    myMap["cherry"] = 8
    fmt.Println("Map after addition:", myMap)
}

3. 检查键是否存在

在Go中,通过索引操作返回值和布尔值可以检查键是否存在。

go 复制代码
package main

import "fmt"

func main() {
    myMap := map[string]int{
        "apple":  5,
        "banana": 3,
    }

    // 检查键是否存在
    value, exists := myMap["orange"]
    if exists {
        fmt.Println("Value for key 'orange':", value)
    } else {
        fmt.Println("Key 'orange' does not exist")
    }
}

4. 删除map元素

可以使用delete()函数从map中删除键值对。

go 复制代码
package main

import "fmt"

func main() {
    myMap := map[string]int{
        "apple":  5,
        "banana": 3,
        "cherry": 8,
    }

    fmt.Println("Map before deletion:", myMap)

    // 删除键为"banana"的元素
    delete(myMap, "banana")

    fmt.Println("Map after deletion:", myMap)
}

5. 遍历map

使用for range循环可以遍历map中的键值对。遍历map时的顺序是随机的。

go 复制代码
package main

import "fmt"

func main() {
    myMap := map[string]int{
        "apple":  5,
        "banana": 3,
        "cherry": 8,
    }

    for key, value := range myMap {
        fmt.Printf("Key: %s, Value: %d\n", key, value)
    }
}

6. 嵌套map

map可以嵌套使用,即值也可以是map类型。

go 复制代码
package main

import "fmt"

func main() {
    nestedMap := map[string]map[string]int{
        "fruits": {
            "apple":  5,
            "banana": 3,
        },
        "vegetables": {
            "carrot": 10,
            "beet":   4,
        },
    }

    fmt.Println("Nested Map:", nestedMap)
    fmt.Println("Value for fruits -> apple:", nestedMap["fruits"]["apple"])
}

7. 初始化空map

在Go中,如果使用var声明map,需要使用make()初始化后才能使用,否则会导致运行时错误。

go 复制代码
package main

import "fmt"

func main() {
    // 使用var声明,但未初始化
    var myMap map[string]int

    // 会引发panic错误,不能直接赋值
    // myMap["apple"] = 5 // 错误

    // 使用make初始化
    myMap = make(map[string]int)
    myMap["apple"] = 5
    fmt.Println("Initialized Map:", myMap)
}

8. map的并发访问

map不是并发安全的,多个goroutine同时访问同一个map可能导致竞态条件或崩溃。如果需要并发访问,请使用sync.Map或者添加互斥锁(sync.Mutex)。

go 复制代码
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    var mu sync.Mutex
    myMap := make(map[int]int)

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            mu.Lock()
            myMap[i] = i * 10
            mu.Unlock()
        }(i)
    }

    wg.Wait()
    fmt.Println("Map after concurrent access:", myMap)
}

总结

  • map声明和初始化 :可以使用make()或字面量语法。
  • 访问和修改:通过键进行访问,支持更新和添加。
  • 删除键 :使用delete()函数。
  • 检查键是否存在:使用索引返回的布尔值。
  • 遍历map :使用for range,但顺序不保证。
  • 并发访问map不是并发安全的,需使用同步机制。
相关推荐
程序员大雄学编程1 小时前
「机器学习笔记7」决策树学习:从理论到实践的全面解析(上)
笔记·决策树·机器学习
聪明的笨猪猪2 小时前
Java Spring “Bean” 面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
bnsarocket3 小时前
Verilog和FPGA的自学笔记3——仿真文件Testbench的编写
笔记·fpga开发·verilog·自学
丰锋ff3 小时前
2025 年真题配套词汇单词笔记(考研真相)
笔记·考研
小熊猫程序猿5 小时前
Datawhale 算法笔记 AI硬件与机器人大模型 (五) Isaac Sim 入门
人工智能·笔记·机器人
不太可爱的叶某人9 小时前
【学习笔记】kafka权威指南——第10章 监控kafka (7-10章只做了解)
笔记·学习·kafka
张人玉10 小时前
C# TCP 客户端开发笔记(TcpClient)
笔记·tcp/ip·c#
不太可爱的叶某人13 小时前
【学习笔记】kafka权威指南——第6章 可靠的数据传递
笔记·学习·kafka
研猛男14 小时前
0、FreeRTOS编码和命名规则
笔记·stm32·freertos