Go从入门到精通——切片(slice)(动态分配大小的连续空间)

切片(slice)(动态分配大小的连续空间)

  Go 语言切片的内部结构包含地址、大小和容量。切片一般用于快速地操作一块数据集合。

1.1、从数组或切片生成新的切片

  切片默认指向一段连续内存区域,可以是数组,也可以是切片本身。

  从连续内存区域生成切片是常见的操作。格式如下:

slice [开始位置:结束位置]
//slice 表示目标切片对象。
//开始位置对应目标切片对象的索引。
//结束位置对应目标切片的结束索引。

  从数组生成切片例子:

1
2
3
4
5
6
7
8
9
10
package main
 
import (
    "fmt"
)
 
func main() {
    var a = [3]int{1, 2, 3} //一个变量a的数组,数组大小是3,已初始化数值1,2,3.
    fmt.Println(a, a[1:2])  //使用 a[1:2]生成一个新切片
}

  从数组或切片生成新的切片拥有如下特性:a :=[]int[1,2,3,4,5,6,7,8,9,10]

  • 取出的元素数量为:结束位置 - 开始位置
  • 取出元素不包含结束位置对应的索引,切片最后一个元素使用 slice[len(slice)] 获取。
  • 当缺省开始位置时,表示从连续区域开头到结束位置,比如 a[:10]。
  • 当缺省结束位置时,表示从开始位置到整个连续区域末尾,比如 a[1:]。
  • 两者时同时缺省时,与切片本身等效,比如:a[:]。
  • 两者同时为 0 时,等效于空切片,一般用于切片复位,比如 a[0:0]
  • 根据索引位置取切片 slice 元素值时,取值范围是(0~len(slice)-1),超界会报运行时错误。生成切片时,结束位置可以填写 len(slice) 但不会报错。

1.2、声明切片

  每一种类型都可以拥有其切片类型,表示多个类型元素的连续集合。因此切片类型也可以被声明。

  切片类型声明格式如下:

var name []T

//name 表示切片类型的变量名
//T 表示切片类型声明的使用过程

1.3、使用 make() 函数构造切片

  如果需要动态地创建一个切片,可以使用 make() 内建函数,格式如下:

make( []T, size, cap)
// T: 切片的元素类型。
// size: 就是为这个类型分配多少个月元素。
// cap: 预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题。

 注意:

  • 使用 make() 函数生成的切片一定是发生了内存分配操作。
  • 给定开始与结束位置(包括切片复位)的切片只是将新的切片结构指向已经分配好的内存区域,设定开始与结束位置,不会发生内存分配操作。
  • 切片不一定就必须经过 make() 函数才能使用。
  • 生成的切片,声明后使用 append() 函数均可以正常使用切片。

1.4、使用 append() 函数为切片添加元素

  Go 语言的内建函数 append() 可以为切片动态添加元素。每个切片会指向一个内存空间,这片空间能容纳一定数量的元素。当空间不能再容纳足够多的元素时,切片就会进行 "扩容"。"扩容" 操作往往发生在 append() 函数调用时。

  切片在扩容时,容量的扩展规律按容量的 2 倍数扩充,例如 1、2、4、8、16……,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
 
import "fmt"
 
func main() {
    var numbers []int
 
    for i := 0; i < 10; i++ {
        numbers = append(numbers, i)
        fmt.Printf("len: %d\t cap:%d pointer: %p\n", len(numbers), cap(numbers), numbers)
    }
 
}

   append() 函数除了添加一个元素外,也可以一次性添加很多元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
 
import "fmt"
 
func main() {
    var car []string
 
    // 添加 1 个元素
    car = append(car, "热烈欢迎世界各地") //使用 append() 函数向切片中添加 1 个元素
    fmt.Println("添加 1 个元素:", car)
 
    // 添加 2 个元素
    car = append(car, "参加", "2022北京冬奥会", "的代表团!") //使用 append() 函数向切片中添加 多 个元素
    fmt.Println("添加 2 个元素:", car)
 
    // 添加切片
    team := []string{"Warmly welcome the delegations from all over the world to the 2022 Beijing Winter Olympic Games!"}
    car = append(car, team...) //在 team 后面加上了 "...",表示将 team 整个添加到 car 的后面。
    fmt.Println(car)
 
}

1.5、复制切片元素到另一个切片

  使用 Go 语言内建的 copy() 函数,可以迅速地将一个切片的数据复制到另外一个切片空间中,copy() 函数的使用格式如下:

copy(destSlice, srcSlice []T) int
//srcSlice 为数据来源切片;
//destSlice 为复制目标(来源的切片复制给谁)。目标切片必须分配过空间且足够承载复制的元素个数。来源和目标的类型一致,copy 的返回值表示实际发生复制的元素个数。

  下面的代码将演示对切片的引用和复制操作后对切片元素的影响:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package main
 
import "fmt"
 
func main() {
 
    //设置元素数量为 1000
    const elementCount = 1000
 
    //预分配足够多的元素切片
    srcData := make([]int, elementCount)
 
    //将切片赋值
    for i := 0; i < elementCount; i++ {
        srcData[i] = i //srcData 切片中存储 1000 个元素,元素值分别是连续的 1-1000 数字
    }
 
    fmt.Printf("srData 切片的值: %d\n", srcData)
 
    //引用切片数据
    //定义一个变量,并将 srcData 值传递给它。切片赋值,实际上只是复制了地址,refData 引用了 srcData的地址,因此 srcData的任何修改,都会影响 refData
    refData := srcData
    fmt.Printf("refData 的内存地址: %p\nsrcData 的内存地址: %p\n", &refData, &srcData)
 
    //预分配足够多的元素切片
    copyData := make([]int, elementCount)
    fmt.Printf("copyData 的内存地址: %p\n", &copyData)
 
    //将数据复制到新的切片空间中
    copy(copyData, srcData)
    fmt.Printf("复制 srcData 后的 copyData 的内存地址: %p\n", &copyData)
 
    //修改原始数据的第一个元素
    srcData[0] = 999
    fmt.Printf("修改 srcData 第 1 个元素后的 copyData 的内存地址: %p\n", &copyData)
 
    //打印引用切片的一个元素
    fmt.Printf("打印引用切片( refData )的一个元素: %d 内存地址: %p\n", refData[0], &refData)
 
    //打印复制切片的第一个和最后一个元素
    fmt.Printf("打印复制切片( copyData )的一个元素: %d 内存地址: %p\n", copyData[0], &copyData)
 
    //复制原始数据从 4 到 6(不包含)
    for i := 0; i < 5; i++ {
        fmt.Printf("%d", copyData[i])
    }
    fmt.Println(srcData[4:6])
     
    fmt.Println(srcData)
 
    copy(copyData, srcData[4:6])
    for i := 0; i < 5; i++ {
        fmt.Printf("%d", copyData[i])
    }
}

  代码输出如下:

 

1.6、从切片中删除元素

  Go 语言中并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main
 
import "fmt"
 
func main() {
 
    seq := []string{"a", "b", "c", "d", "e"}
 
    //指定删除位置
    index := 2
 
    //查看删除位置之间的元素和之后的元素
    fmt.Println(seq[:index], seq[index+1:])
 
    // 将删除点前后的元素连接起来
    seq = append(seq[:index], seq[index+1:]...) //后面这3个点要加上,不然会报错,用于两个切片合并的。
 
    fmt.Println(seq)
}

posted @   左扬  阅读(1200)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
levels of contents
点击右上角即可分享
微信分享提示