golang 数组 切片slice append copy 映射map 列表list

数组

  • 数组的创建后,长度是固定的。
  • 数组是值类型,数组的赋值都是拷贝。
  • 数组的长度也是类型的一部分。[10]int,[20]int是不同的类型
go 复制代码
package main

import (
        "fmt"
)

func main() {
        // 定义数组,数组是包含了数组长度和类型。
        // 所以下面是两种不通的数组类型
        var array1 [3]int
        var array2 [4]int

        // 数组的初始化
        array1[0] = 10
        array2[0] = 1
        array2[1] = 2

        // 使用数组的字面量创建数组
        // 虽然长度是3,但是可以值给2个赋值,剩下的第三个元素默认为0
        array3 := [3]int{100, 200}

        // 可以推断数组长度,可以省略
        array4 := [...]int{100, 200}

        fmt.Println(array1, array2, array3, array4)

        // 也可以根据索引,只给其中的某些指定的索引位赋值
        array5 := [10]int{0: 12, 9: 100}
        fmt.Println(array5)

        // 如果自动推断数组长度,则根据最大的索引为数组的最大长度
        // 这里最大的索引为3,则数组的长度为4
        array6 := [...]int{0: 20, 3: 1}
        fmt.Println(array6)

}

数组的遍历,多维数组

css 复制代码
package main

import (
        "fmt"
)

func main() {
        // 遍历数组
        var array1 = [10]int{}
        for i := 0; i < 10; i++ {
                array1[i] = i * 10
        }
        for i := 0; i < 10; i++ {
                fmt.Println(array1[i])
        }

        // 多维数组
        var array2 [3][2]int
        // 这里大括号前面还得写类型
        array2 = [3][2]int{{1, 2}, {2, 3}, {3, 4}}
        for i := 0; i < 3; i++ {
                for j := 0; j < 2; j++ {
                        fmt.Printf("%v", array2[i][j])
                }
                fmt.Println()
        }
        array3 := [3][2]int{{1, 2}, {2, 3}, {3, 4}}
        for i := 0; i < 3; i++ {
                for j := 0; j < 2; j++ {
                        fmt.Printf("%v", array3[i][j])
                }
                fmt.Println()
        }

}

切片

go 复制代码
package main

import (
        "fmt"
)

func main() {

        // 切片
        // var name []type
        var a1 []int
        var a2 []string
        // 声明切片并赋值
        var a3 = []int{}

        /*
                0 true
                0 true
                0 false
        */
        fmt.Println(len(a1), a1 == nil)
        fmt.Println(len(a2), a2 == nil)
        fmt.Println(len(a3), a3 == nil)

        // 从数组或切片创建一个新的切片
        // slice[beginIndex,endIndex]
        var b1 = [5]int{1, 2, 3, 4, 5}
        b2 := b1[1:2]
        // 因为切片是引用类型,所以会修改原来数组的值
        b2[0] = 100
        fmt.Println(b1)
        //如果不写beginIndex,则默认从开始
        b3 := b1[:4]
        // 如果不写endIndex,默认到最后
        b4 := b1[1:]
        // 如果都不写,则默认所有的值
        b5 := b1[:]
        /*
                b3 [1 100 3 4]
                b4 [100 3 4 5]
                b5 [1 100 3 4 5]
        */
        fmt.Printf("b3 %v\n", b3)
        fmt.Printf("b4 %v\n", b4)
        fmt.Printf("b5 %v\n", b5)
}

append 添加元素,返回新的切片

go 复制代码
package main

import (
        "fmt"
)

func main() {
        // -------------- 第一段:触发扩容 → 互不影响 --------------
        var a = []int{1, 2, 3} // len=3, cap=3
        b := append(a, 20)     // 触发扩容!新数组
        b[0] = 100
        fmt.Println("a =", a)  // [1 2 3]
        fmt.Println("b =", b)  // [100 2 3 20]
        fmt.Println("------------------------")

        // -------------- 第二段:容量足够 → 共用底层数组 → 互相影响 --------------
        var a1 = make([]int, 3, 10) // len=3, cap=10(容量非常足)
        a1[0] = 1
        a1[1] = 2
        a1[2] = 3

        // 不触发扩容!
        b1 := append(a1, 20)
        b1[0] = 100 // 修改的是**共用的底层数组**

        fmt.Println("a1 =", a1) // [100 2 3] → 被改了!
        fmt.Println("b1 =", b1) // [100 2 3 20]
}

内存机制:不扩容的时候,切片的地址还是之前的。扩容后,地址是会变的。

  • 如果当前追加元素后,容量够,则不扩容
  • 如果当前的容量不够,则容量不够,则容量翻倍。此时如果容量还不够,则直接用翻倍后的容量,再向上取2的幂的整数
  • 如果之前的容量小于256,则容量直接翻倍,再向上取2的幂的整数。
  • 如果之前的容量大于等于256,则每次增加(当前容量+ 3* 256)/4,一直到容量能够装下。再向上取2的幂的整数。

copy

go 复制代码
package main

import "fmt"

func main() {
        // 定义源切片 a,长度 4,容量 4
        var a = []int{1, 2, 3, 4}

        // 定义目标切片 b,长度 2,容量 2
        b := make([]int, 2)

        // 执行 copy:把 a 的元素复制到 b 中
        // 规则:复制个数 = min(len(a), len(b)) = min(4,2) = 2 个
        copy(b, a)

        // 所以 b 只会拿到 a 的前 2 个元素:[1 2]
        fmt.Println(b) // 输出:[1 2]

        // ======================================
        // 重点:copy 是深拷贝,两个切片底层数组完全独立
        // ======================================
        b[0] = 100 // 修改 b 的第一个元素

        // a 不受任何影响,还是原来的数据
        fmt.Println(a) // [1 2 3 4]
        // b 已经被修改
        fmt.Println(b) // [100 2]
}

删除

less 复制代码
package main

import "fmt"

func main() {
        // 初始切片 a:底层数组 [1,2,3,4],len=4,cap=4
        var a = []int{1,2,3,4}

        // ==============================================
        // 1. 删除中间元素:append(a[:1], a[2:]...)
        // ==============================================
        // 内存行为:
        // a[:1] len=1, cap=3(共享原数组)
        // a[2:] 是切片,共享原数组
        // append 发现 a[:1] 的容量足够(cap=3 >= 需要存3个元素)
        // ✅ 【不分配新内存】【不创建新数组】,直接覆盖原数组!
        // 原数组被覆盖成:[1,3,4,4]
        b := append(a[:1], a[2:]...)
        fmt.Println(a)  // [1 3 4 4] 原数组被修改!
        fmt.Println(b)  // [1 3 4]

        // 性能:O(k) k=复制元素数量,**无内存分配**,极快


        // ==============================================
        // 2. 重置切片,全新数组
        // ==============================================
        a = []int{1,2,3,4}

        // ==============================================
        // 删除索引=2的元素 3:a[:2 + copy(a[2:], a[3:])]
        // ==============================================
        // 内存行为:
        // copy 是【原地覆盖】,把后面的元素往前挪
        // 完全不创建新数组,不分配内存
        // 数组变成:[1,2,4,4]
        // copy 返回复制数量 1
        // 最终切片取 a[:3],还是共享原数组
        b1 := a[:2+copy(a[2:], a[3:])]
        fmt.Println(a)  // [1 2 4 4]
        fmt.Println(b1) // [1 2 4]

        // 性能:O(k) 【无内存分配】⭐⭐⭐⭐⭐
        // Go 最快的【原地删除】写法,没有之一!


        // ==============================================
        // 重置切片
        // ==============================================
        a = []int{1,2,3,4}

        // ==============================================
        // 3. 删除开头:c := a[2:]
        // ==============================================
        // 内存行为:
        // 只是创建新切片头,指针后移
        // ✅ 【不复制数据】【不分配数组】
        c := a[2:]
        fmt.Println(c) // [3 4]

        // 性能:O(1) 最快!⭐⭐⭐⭐⭐
        // 缺点:共享底层数组,原切片大时可能内存泄漏


        // ==============================================
        // 4. 删除开头:append(a[:0], a[2:]...)
        // ==============================================
        // 内存行为:
        // a[:0] len=0,cap=4(容量足够)
        // append 直接【原地覆盖】原数组
        // 不分配新数组!
        // 数组变成 [3,4,3,4]
        c1 := append(a[:0], a[2:]...)
        fmt.Println(c1) // [3 4]

        // 性能:O(k) 【无内存分配】,极快


        // ==============================================
        // 5. 删除开头:a[:copy(a, a[2:])]
        // ==============================================
        // 内存行为:
        // copy 原地复制,【无任何新内存】
        // 数组被覆盖成 [3,4,3,4]
        c2 := a[:copy(a, a[2:])]
        fmt.Println(c2) // [3 4]

        // 性能:O(k) 【无内存分配】⭐⭐⭐⭐⭐
        // 最推荐的安全、高效删除


        // ==============================================
        // 6. 删除尾部:d := a[:2]
        // ==============================================
        // 内存行为:
        // 切片截取,【不修改底层数组】
        d := a[:2]
        fmt.Println(d) // [3 4]

        // 性能:O(1)
}

映射map

go 复制代码
package main

import (
        "fmt"
)

func main() {
        // 声明map
        var map1 map[string]string
        //初始化map
        map1 = make(map[string]string)

        map2 := map[string]string{"name": "libai", "addr": "china"}
        fmt.Println(map1)
        fmt.Println(map2)

        // map的遍历
        for index, value := range map2 {
                fmt.Println(index, value)
        }

        // 删除元素
        delete(map2, "name")
        fmt.Println(map2)
}

列表list

go 复制代码
package main

import (
        "container/list"
        "fmt"
)

func main() {
        // 列表,内部实现是双链表
        var list1 = list.New()

        // 此时处于零值状态(延迟初始化)
        var list2 list.List

        // 【输出1】打印初始的空链表 list1 (指针类型,长度为0)
        fmt.Println(list1)
        // &{{0x5108dba780f0 0x5108dba780f0 <nil> <nil>} 0}

        // 【输出2】打印零值状态的 list2 (非指针,内部全为nil,长度为0)
        fmt.Println(list2)
        // {{<nil> <nil> <nil> <nil>} 0}

        hello := list1.PushBack("Hello") // 尾部插入 "Hello"
        hi := list1.PushFront("hi")      // 头部插入 "hi"

        // 【输出3】打印插入两个元素后的 list1 (长度变为2)
        fmt.Println(list1)
        // &{{0x5108dba781b0 0x5108dba78180 <nil> <nil>} 2}

        // 第一次遍历链表并打印元素的 Value
        for i := list1.Front(); i != nil; i = i.Next() {
                fmt.Println(i.Value)
        }
        // 【输出4】按插入顺序打印:先插的 hi 在头,后插的 Hello 在尾
        // hi
        // Hello

        // 在 hello 节点之后插入 "China"
        list1.InsertAfter("China", hello)
        // 在 hi 节点之前插入 "你好"
        list1.InsertBefore("你好", hi)

        // 第二次遍历链表并打印元素的 Value
        for i := list1.Front(); i != nil; i = i.Next() {
                fmt.Println(i.Value)
        }
        // 【输出5】最终链表顺序:你好 -> hi -> Hello -> China
        // 你好
        // hi
        // Hello
        // China
}

删除元素

scss 复制代码
//参数为element,每次插入会后返回这个element
list.Remove(hi)
相关推荐
No8g攻城狮1 天前
【AI工具】wsl2 + ubuntu22.04安装部署sub2api详细教程
人工智能·ai·go·vue
明月_清风2 天前
Go 没有 `class`,如何实现面向对象三要素?与传统 OOP 的深度对比
后端·go
审判长烧鸡2 天前
【GO context 】上下文取消/超时的本质
go·context·上下文·ai问答
m0_502724953 天前
Go 语言 defer 在命名返回值 和 匿名返回值 函数中的表现不一样
go
java知路3 天前
解决 Go 编译速度慢的问题
go
审判长烧鸡3 天前
【Go Interface】接口诞生的意义
go·接口·interface
审判长烧鸡3 天前
【Go i18n】TOML语言包
go·i18n·语言包
用户398346161204 天前
Go-Spring 实战第 10 课 —— 依赖注入的方式:字段注入和构造函数注入
spring·go
用户398346161204 天前
Go-Spring 实战第 9 课 —— IoC 容器:复杂 Go 应用如何统一对象装配
spring·go