二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Shell编程

  • Go编程笔记

  • 前端编程笔记

  • Go学习笔记

    • 基础部分

      • 基础知识
      • 数据类型
      • fmt的格式化输入输出
      • 运算符
      • 流程控制
      • 认识函数
      • 包的操作
      • 项目工程-在同级目录
      • 指针(pointer)
      • 数组(array)
      • 切片(slice)
        • 1,概述。
        • 2,认识切片。
        • 3,数组和切片的区别。
        • 4,切片的初始化。
        • 5,切片截取。
        • 6,切片和底层数组的关系。
        • 7,内建函数。
          • 1,append。
          • 1,append 简单使用。
          • 2,append 扩容特点。
          • 2,copy。
        • 8,切片作为函数参数。
        • 9,写一个猜数字游戏。
          • 1,生成一个 4 位的随机数。
          • 2,取出每一个数。
          • 3,最终成型的样子。
      • 字典(map)
      • 结构体(struct)
      • 匿名组合
      • 方法
      • 接口
      • error接口
      • panic
      • recover
      • 字符串操作
      • 字符串转换
      • 正则表达式
      • JSON处理
      • 文件的操作
      • goroutine
      • channel
      • select
      • sync-lock锁
      • 网络概述
      • Socket编程
      • 发文件与聊天室
      • http编程
      • 爬虫实战
      • 单元测试了解
    • web框架

    • orm框架

  • Vue-21年学习笔记

  • Vue-22年重学笔记

  • 编程世界
  • Go学习笔记
  • 基础部分
二丫讲梵
2021-07-09
目录

切片(slice)

文章发布较早,内容可能过时,阅读注意甄别。

# 1,概述。

数组的长度在定义之后无法再次修改,数组是值类型,每次传递都将产生一份副本。显然这种数据结构无法满足开发者的真实需求。Go 语言提供了数组切片(slice)来弥补数组的不足。

切片并不是数组或数组指针,它通过内部指针和相关属性引用数组片段,以实现边长方案。

slice 并不是真正意义上的动态数组,而是一个引用类型。slice 总是指向一个底层 array,slice 的声明也可以像 array 一样,只是不需要长度。

# 2,认识切片。

m_bd2b5f11d0694dfc63cbf270e566fcf2_r

a := [...]int{1,2,3,4,5}
s := a[0:3:5]

定义一个数组a,然后通过s对其进行切片。
a[0:3:5]表示:a[low:high:max]
low:表示下标的起点,如果是0,则从第一个开始。
high:表示下标的终点,(不包括此下标),那么实际终点应该是high-1

得有两个概念:长度和容量。
长度表示切片的长度=high-low
容量表示切片的容量=max-low
1
2
3
4
5
6
7
8
9
10
11

用代码举例:

package main

import "fmt"

func main() {
	a := [5]int{1, 2, 3, 4, 5}
	s := a[0:3:5]
	fmt.Println("s = ", s)          //切片的内容
	fmt.Println("len(s) =", len(s)) //切片的长度=3-0
	fmt.Println("cap(s) =", cap(s)) //切片的容量=5-0

	s = a[1:4:5]
	fmt.Println("s = ", s)          //从下标1开始,取4-1=3个
	fmt.Println("len(s) =", len(s)) //切片的长度=4-1
	fmt.Println("cap(s) =", cap(s)) //切片的容量=5-1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3,数组和切片的区别。

package main

import "fmt"

func main() {
	//切片和数组的区别
	//数组[]里面的长度是一个固定的常量,数组不能修改长度,len和cap永远都是5
	a := [5]int{}
	fmt.Printf("len = %d, cap = %d\n", len(a), cap(a))

	//切片的[]里面为空,或者为...
	//切片的长度或容量不固定
	s := []int{}
	fmt.Printf("1:len = %d, cap = %d\n", len(s), cap(s)) //如果没定义,则默认为0

	s = append(s, 3) //append表示给切片末尾追加一个元素
	fmt.Printf("2:len = %d, cap = %d\n", len(s), cap(s))

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 4,切片的初始化。

package main

import "fmt"

func main() {
	//自动推导类型,同时初始化
	s := []int{1, 2, 3, 4}
	fmt.Println("s = ", s)

	//借助make函数,格式为: make(切片类型,长度,容量)
	s1 := make([]int, 5, 10)
	fmt.Printf("len = %d, cap = %d\n", len(s1), cap(s1))

	//其中容量可以省略,没写的话,容量等于长度
	s2 := make([]int, 5)
	fmt.Printf("len = %d, cap = %d\n", len(s2), cap(s2))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5,切片截取。

操作 含义
s[n] 切片 s 中下标为 n 的项
s[:] 从切片 s 的下标为 0 到 len(s)-1 处所获得的切片内容
s[low:] 从切片 s 的下标为 low 到 len(s)-1 处所获得的切片
s[:high] 从切片 s 的下标为 0 到 high 处所获得的切片
s[low:high] 从切片 s 的下标为 low 到 high 处所获得的切片
s[low:high:max] 从切片 s 的下标为 low 到 high 处所获得的切片
len(s) 切片 s 的长度,总是<=cap(s)
cap(s) 切片 s 的容量,总是>=len(s)

示例说明:

有如下切片类型:

a := []int{0,1,2,3,4,5,6,7,8,9}
1

那么:

操作 结果 len map 说明
a[:] [0 1 2 3 4 5 6 7 8 9] 10 10 等价于[0:len(a):cap(a)]
a[3] [2] 无 无 无
a[3:6:7] [3 4 5] 3 4 如见
a[:6] [0 1 2 3 4 5] 6 10 等价于[0:6:cap(a)] 常用
a[5:] [5 6 7 8 9] 5 5 等价于[5:len(a):cap(a)]

代码示例如下:

package main

import "fmt"

func main() {
	a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

	s1 := a[:] //等价于[0:len(a):cap(a)]
	fmt.Println("s1=", s1)
	fmt.Printf("len=%d, cap=%d\n", len(s1), cap(s1))

	//操作切片中的某个元素,方法和数组的一样
	s2 := a[5]
	fmt.Println("s2=", s2)

	s3 := a[3:6:7] //[a[3], a[4], a[5]] len=6-3 cap=7-3
	fmt.Println("s3=", s3)
	fmt.Printf("len=%d, cap=%d\n", len(s3), cap(s3))

	s4 := a[:6] //等价于[0:6:cap(a)] len=6-0 cap=10-0
	fmt.Println("s4=", s4)
	fmt.Printf("len=%d, cap=%d\n", len(s4), cap(s4))

	s5 := a[3:] //等价于[3:len(a):cap(a)]
	fmt.Println("s5=", s5)
	fmt.Printf("len=%d, cap=%d\n", len(s5), cap(s5))
}
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

# 6,切片和底层数组的关系。

package main

import "fmt"

func main() {
	a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

	//新切片
	s1 := a[2:5]
	s1[1] = 666
	fmt.Println("s1 =", s1)
	fmt.Println("a =", a)

	//另一新切片
	s2 := s1[1:7]
	fmt.Println("s2 =", s2)
	s2[2] = 777
	fmt.Println("a =", a)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

上边程序输出结果为:

$ go run 19_切片和底层数组的关系.go
s1 = [2 666 4]
a = [0 1 2 666 4 5 6 7 8 9]
s2 = [666 4 5 6 7 8]
a = [0 1 2 666 4 777 6 7 8 9]
1
2
3
4
5

第一个切片 s1 的结果还是比较容易理解的,但是到了第二个切片 s2 这里,有稍微有点让人感到费解了,而这,正式这一小节标题的意义,切片与底层数组的关系。

可画示意图帮助理解:

m_8e3e133e049627f70aca174d55362f93_r

# 7,内建函数。

# 1,append。

# 1,append 简单使用。

append 函数用于向 slice 尾部追加元素。

package main

import "fmt"

func main() {
	a := []int{}
	fmt.Printf("len = %d, cap = %d, a =%d\n", len(a), cap(a), a)
	//在原切片的末尾添加元素
	a = append(a, 1)
	a = append(a, 2, 3)
	fmt.Printf("len = %d, cap = %d, a =%d\n", len(a), cap(a), a)

	b := make([]int, 5)
	fmt.Printf("len = %d, cap = %d, b =%d\n", len(b), cap(b), b)
	b = append(b, 6)
	fmt.Printf("len = %d, cap = %d, b =%d\n", len(b), cap(b), b)

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2,append 扩容特点。

append 函数会智能地控制底层数组的容量增长,一旦超过原底层数组容量,通常以原容量 2 倍的数值定义给新容量,并复制原来的数据。

package main

import "fmt"

func main() {
	//如果超过原来的容量,通常以2倍容量扩容
	s := make([]int, 0, 1)
	oldCap := cap(s)
	for i := 0; i < 10; i++ {
		s = append(s, i)
		if newCap := cap(s); oldCap < newCap {
			fmt.Printf("cap: %d===> %d\n", oldCap, newCap)
			oldCap = newCap
		}
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

大概有如下特征:

  • 首先判断,如果新申请的容量(cap)大于 2 倍的旧容量(old.cap),那么最终容量(newcap)就是新申请容量的容量(cap)。
  • 否则判断,如果旧切片的长度小于 1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap)。
  • 否则判断,如果旧切片长度大于等于 1024,则最终容量(newcap)从旧容量(old.cap)的 1/4 循环增加,即(newcap=old.cap,for {newcap+=newcap/4}),直到最终容量大于等于新申请的容量。
  • 如果最终容量计算溢出,则最终容量就是新申请容量(cap)。

需要注意的是,切片扩容还会根据切片中元素的类型不同而作不同的处理,比如int和string类型的处理方式就不一样。

package main

import "fmt"

func main() {
	s1 := []string{"aaa", "bbb", "ccc"}
	fmt.Println(s1)
	fmt.Printf("len=%d,cap=%d\n", len(s1), cap(s1))
	s1 = append(s1, "ddd")
	fmt.Println(s1)
	fmt.Printf("len=%d,cap=%d\n", len(s1), cap(s1))
	s2 := []string{"aa", "bb", "cc"}
	s1 = append(s1, s2...) //...表示展开s2这个数组/切片
	fmt.Println(s1)
	fmt.Printf("len=%d,cap=%d\n", len(s1), cap(s1))
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2,copy。

package main

import "fmt"

func main() {
	src := []int{1, 2}
	dst := []int{6, 6, 6, 6, 6, 6}
	copy(dst, src) //表示将src的内容copy给dst,然后替换1,2对应位置的元素
	fmt.Println("dst =", dst)

}
1
2
3
4
5
6
7
8
9
10
11

函数 copy 在两个 slice 间复制数据,复制长度以 len 小的为准,两个 slice 可指向同一底层数组。

package main

import "fmt"

func main() {
	a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

	b := a[8:]
	c := a[:5]
	fmt.Printf("a = %d,b = %d,c = %d\n", a, b, c)
	copy(c, b)
	fmt.Println("c = ", c)
	fmt.Println("a = ", a)

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 8,切片作为函数参数。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

//生成几个随机数
func InitData(s []int) {
	//设置种子
	rand.Seed(time.Now().UnixNano())

	for i := 0; i < len(s); i++ {
		s[i] = rand.Intn(100) //100以内的10个随机数
	}
}

//冒泡排序
func BubbleSort(s []int) {
	for i := 0; i < len(s); i++ {
		for j := 0; j < len(s)-1-i; j++ {
			if s[j] > s[j+1] {
				s[j], s[j+1] = s[j+1], s[j]
			}
		}
	}
}

func main() {
	n := 10
	//创建一个切片,len为n
	s := make([]int, n)
	fmt.Println("s =", s)

	InitData(s)
	fmt.Println("排序前: ", s)

	BubbleSort(s)
	fmt.Println("排序后: ", s)
}
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

切片做函数参数的时候,是引用传递,当函数对切片进行操作之后,切片的内容也会随之改变。

# 9,写一个猜数字游戏。

# 1,生成一个 4 位的随机数。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

//生成几个随机数
func CreateNum(p *int) {
	//设置种子
	rand.Seed(time.Now().UnixNano())
	var num int
	for {
		num = rand.Intn(10000)
		if num >= 1000 {
			break
		}
	}
	*p = num
}

func main() {
	var randNum int
	//产生一个4位随机数
	CreateNum(&randNum)
	fmt.Println("randNum: ", randNum)
}
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

# 2,取出每一个数。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

//生成几个随机数
func CreateNum(p *int) {
	//设置种子
	rand.Seed(time.Now().UnixNano())
	var num int
	for {
		num = rand.Intn(10000)
		if num >= 1000 {
			break
		}
	}
	*p = num
}

//取出每一位
func GetNum(s []int, num int) {
	s[0] = num / 1000       //取千位
	s[1] = num % 1000 / 100 //取百位
	s[2] = num % 100 / 10   //取十位
	s[3] = num % 10

}
func main() {
	var randNum int
	//产生一个4位随机数
	CreateNum(&randNum)
	fmt.Println("randNum: ", randNum)

	randSlice := make([]int, 4)
	//保存着个4位数的每一位
	GetNum(randSlice, randNum)
	fmt.Println("randSlice: ", randSlice)

}
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

# 3,最终成型的样子。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

//生成几个随机数
func CreateNum(p *int) {
	//设置种子
	rand.Seed(time.Now().UnixNano())
	var num int
	for {
		num = rand.Intn(10000)
		if num >= 1000 {
			break
		}
	}
	*p = num
}

//取出每一位
func GetNum(s []int, num int) {
	s[0] = num / 1000       //取千位
	s[1] = num % 1000 / 100 //取百位
	s[2] = num % 100 / 10   //取十位
	s[3] = num % 10

}

func OnGame(randSlice []int) {
	var num int
	keySlice := make([]int, 4)
	for {
		for {
			fmt.Printf("请输入一个4位数:")
			fmt.Scan(&num)
			//限定输入的范围 999 < num < 10000
			if 999 < num && num < 10000 {
				break
			}
			//fmt.Println("输入的数字不符合规范。")
		}
		//fmt.Println("num = ", num)

		GetNum(keySlice, num)
		//fmt.Println("keySlice = ", keySlice)

		n := 0
		for i := 0; i < 4; i++ {
			if keySlice[i] > randSlice[i] {
				fmt.Printf("第%d位的数字大了\n", i+1)
			} else if keySlice[i] < randSlice[i] {
				fmt.Printf("第%d位的数字小了\n", i+1)
			} else {
				fmt.Printf("第%d位的数字猜对了\n", i+1)
				n++
			}
		}
		if n == 4 {
			fmt.Println("全部都猜对了!!")
			break
		}
	}
}

func main() {
	var randNum int
	//产生一个4位随机数
	CreateNum(&randNum)
	//fmt.Println("randNum: ", randNum)

	//取出这个四位数的每一位
	randSlice := make([]int, 4)
	GetNum(randSlice, randNum)
	//fmt.Println("randSlice: ", randSlice)

	//输入一个四位数
	OnGame(randSlice)

}
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
微信 支付宝
#go
上次更新: 2024/07/04, 22:40:37
数组(array)
字典(map)

← 数组(array) 字典(map)→

最近更新
01
学习周刊-总第213期-2025年第22周
05-29
02
学习周刊-总第212期-2025年第21周
05-22
03
从赵心童世锦赛夺冠聊聊我的斯诺克情缘
05-16
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式