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不是并发安全的,需使用同步机制。
相关推荐
幺零九零零31 分钟前
【Golang】validator库的使用
开发语言·后端·golang
Padid1 小时前
Unity SRP学习笔记(二)
笔记·学习·unity·游戏引擎·图形渲染·着色器
海绵宝宝de派小星2 小时前
Go:接口和反射
开发语言·后端·golang
techdashen2 小时前
Go Modules和 雅典项目
开发语言·后端·golang
=(^.^)=哈哈哈2 小时前
从安全角度看多线程(附Golang举例)
爬虫·python·golang
杜杜的man3 小时前
【go从零单排】go三种结构体:for循环、if-else、switch
开发语言·后端·golang
小c君tt3 小时前
QT中QML学习笔记2
笔记·qt·学习
东方-教育技术博主4 小时前
GraphPad.Prism 9.5中文激活版免费安装和使用-画图和分析软件
笔记
黑风风6 小时前
Ubuntu 22 安装 Apache Doris 3.0.3 笔记
笔记·ubuntu·apache