Go_切片初始化、遍历、截取、修改、append、copy、作为函数参数、求和、求最大值

切片定义与初始化:

  • 切片(slice)通过指针引用底层数组,当需要时,会申请更大的内存空间将当前数据复制过去, 以实现类似动态数组的功能。
  • 切片的长度是不固定的,可以追加数据,切片的底层是一个结构体
  • 切片没有自己的任何数据,它只是底层数组的一个引用,对切片所做的任何修改都将反映在底层数组中。
type slice struct { 
	array unsafe.Pointer //  指向底层数组某元素的指针,也是切片的起始元素
	len int // 切片的长度
	cap int // 切片的容量
}

怎么区分数组和切片:

数组是必须要有长度,不管是显式还是推导都要有长度,但是切片不是一定要显式长度

切片的创建:

可直接创建切片对象,无需预先准备数组。因为是引用类型,必须使用make函数或显式初始化语句,它会自动完成底层数组内存分配

注意:切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3sCHkZTU-1677512960761)(/Users/itzhuzhu/Library/Application Support/typora-user-images/image-20220901152220185.png)]

示例:

s := make([]byte,5)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nxzb2rBx-1677512960762)(/Users/itzhuzhu/Library/Application Support/typora-user-images/image-20230213101616592.png)]

定义切片:

// 仅定义没有写make就相当于创建了切片里面是空的
var slice [] type

格式2:

var slice = []type{}

make函数创建切片:

// len:切片的长度    cap:切片的容量    cap不能大于len,且可省略,默认和len相同
// len和cap:长度是已经初始化的空间,容量是已经开辟的空间,包括已经初始化的空间和空闲的空间
// cap是一个内置函数,用于统计切片的容量,也就是最大可以存放多少个元素

var slice = make([]type, len, cap)

使用make创建的切片是有空间的,非make定义的切片是空的(无法获取地址)

func main() {
	var slice []int             // 普通格式创建的slice,空切片
	slice2 := []int{}           // 格式2创建的sslice,空切片
	slice3 := make([]int, 2)    // make函数创建的slice,省略cap,cap=len
	slice4 := make([]int, 2, 3) // make函数创建的slice,自定义cap

	fmt.Println("slice:", slice)
	//fmt.Println("slice的内存地址:", &slice[0]) // panic: runtime error: index out of range [0] with length 0

	fmt.Println("slice2:", slice2)
	//fmt.Println("slice2的内存地址:", &slice2[0]) // panic: runtime error: index out of range [0] with length 0

	fmt.Println("slice3:", slice3, "len:", len(slice3), "cap:", cap(slice3), "slice3的内存地址:", &slice3[0])
	fmt.Println("slice4:", slice4, "len:", len(slice4), "cap:", cap(slice4), "slice4的内存地址:", &slice4[0])
}

输出:

slice: []
slice2: []
slice3: [0 0] len: 2 cap: 2 slice3的内存地址: 0x14000122010
slice4: [0 0] len: 2 cap: 3 slice4的内存地址: 0x1400012a018

初始化格式:

/// 普通格式创建的切片
var slice []type
var slice = [] type{数据1,数据2,数据3}

// make函数方式创建的切片可以通过append和循环初始化
slice = append(slice,数据1,数据2...)

演示:

func main() {
	var slice []int
	slice = append(slice, 1, 2, 3, 4, 5)
	slice[0] = 100

	var slice2 = []int{}
	slice2 = append(slice2, 1, 2, 3, 4, 5)
	slice2[0] = 100

	slice3 := make([]int, 2, 3)
	slice3 = append(slice3, 1, 2, 3, 4, 5)
	slice3[0] = 100

	fmt.Println("slice:", slice)
	fmt.Println("slice2:", slice2)
	fmt.Println("slice3:", slice3)
}

输出:

slice: [100 2 3 4 5]
slice2: [100 2 3 4 5]
slice3: [100 0 1 2 3 4 5]

引用数组创建切片:

func main() {
	// 1. 创建数组
	arr := [...]int{1, 2, 3, 4, 5}

	// 2.创建切片并引用数组
	slice := arr // 相当于引用数组全部数据

	// arr[1:3]:引用了数组arr的数据,从索引1到索引3-1(包含索引1但是不包含索引3,也就是arr[2,3])
	slice2 := arr[1:3]
	fmt.Println("slice:", slice)

	// 现在切片中只有 [2 3],也就是索引0和1,如果用取slice[2]就会越界
	fmt.Println("slice2:", slice2)
	//fmt.Println("slice:", slice[2]) // 报错

	fmt.Println("slice2中元素的个数:", len(slice2))
	fmt.Println("slice2的容量:", cap(slice2))
}

输出:

slice: [1 2 3 4 5]
slice2: [2 3]
slice2中元素的个数: 2
slice2的容量: 4

当多个切片共享相同的底层数组时,修改任意切片既为修改底层数组

func main() {
	slice := make([]int, 2, 3)
	slice[0] = 1
	slice[1] = 2
  
	slice2 := slice
	slice2[0] = 100

	fmt.Println(slice)
	fmt.Println(slice2)
}

输出:

[100 2]
[100 2]

注意下面两种定义方式的区别。前者仅定义了一个[ ]int类型变量,并未执行初始化操作,而后者则用初始化表达式完成了全部创建过程

func main() {
	var a []int
	b := []int{}
	fmt.Println(a == nil, b == nil) // true false
}

二维切片

func main() {
	// 有任意个一维切片,每个一维切片中有任意个数据
	x := [][]int{
		{1, 2},
		{10, 20, 30},
		{100},
	}
	fmt.Println(x[0])
	fmt.Println(x[1])
	fmt.Println(x[2])
}

输出:

[1 2]
[10 20 30]
[100]

切片遍历:

遍历和数组一样可以使用普通的for循环和range遍历得到

func main() {
	slice := []int{1, 2, 3, 4, 5}
	for i := 0; i < len(slice); i++ {
		fmt.Print(slice[i])
	}
	
	for _, v := range slice {
		fmt.Println(v)
	}
}

append函数:

append函数是向切片的末尾添加数据,如果添加的内容超出了切片初始定义的容量,切片会自动扩容,每次扩容都是一个新的内存,和原切片无关联,所以如果是通过参数传递的方式,使用append添加数据,但是不会影响到原切片的数据,原因就是append每次拓展都是一个新的空间,指向的内存不再是原切片。

切片append底层原理:

  1. 创建新的数组并扩容
  2. 将原数组的数据拷贝到新数组
  3. 将新添加的数据添加到新数组
  4. slice引用到新数组
  5. 原数组被程序销毁

格式:

slice = append(slice,数据) 

演示:

func main() {
	// 定义了切片的长度是3,初始容量是4,系统会对长度赋上默认值,int类型就是0,所以打印3个0
	slice := make([]int, 3, 4)
	fmt.Println("初始切片的数据:", slice, "长度:", len(slice))

	// 添加数据,此时容量是4,数据已经有4个了,如果继续多加点数据,会不会报错
	slice = append(slice, 1)
	fmt.Println("第一次添加数据:", slice, "长度:", len(slice))

	slice = append(slice, 2, 3, 4)
	fmt.Println("第二次添加数据:", slice, "长度:", len(slice))

	// slice...:添加slice全部数据
	slice = append(slice, slice...)
	fmt.Println("第三次添加数据:", slice, "长度:", len(slice))

	// 再创建一个切片,初始化后把slice的数据添加过来,注意,这个不是复制,是在结尾继续添加
	slice2 := make([]int, 3, 4)
	slice2 = append(slice2, slice...)
	fmt.Println("slice2:", slice2, "长度:", len(slice2))
}

输出:

初始切片的数据: [0 0 0] 长度: 3
第一次添加数据: [0 0 0 1] 长度: 4
第二次添加数据: [0 0 0 1 2 3 4] 长度: 7
第三次添加数据: [0 0 0 1 2 3 4 0 0 0 1 2 3 4] 长度: 14
slice2: [0 0 0 0 0 0 1 2 3 4 0 0 0 1 2 3 4] 长度: 17

copy函数:

内置函数copy,可以对切片进行拷贝,但切片是引用类型,新切片复制后,修改数据是不会影响原切片的。

copy只是复制索引相对应的数据,覆盖对应索引数据,如果长度不够,不会覆盖原来的数据,

格式:

// 如果切片1容量不够,则不复制剩余数据。如果切片1的数据比切片2多,从切片2复制的数据有多少,复制多少
copy(切片1,切片2) // 把切片2的数据复制到切片1中

从切片2复制到切片1,但是切片2的数据比切片1的多,所以,最终只是复制了一部分,也就是索引相对应的数据

func main() {
	slice := []int{1, 2, 3}
	slice2 := []int{4, 5, 6, 7, 8, 9}
	copy(slice, slice2) // 把切片2复制到切片1
	fmt.Println("slice:s", slice)

	// 修改数据
	slice = append(slice, 97, 98)
	slice2 = append(slice2, 100)
	fmt.Println("修改数据后:slice中的数据:", slice)
	fmt.Println("修改数据后:slice2中的数据:", slice2)
}

输出:

slice:s [4 5 6]
修改数据后:slice中的数据: [4 5 6 97 98]
修改数据后:slice2中的数据: [4 5 6 7 8 9 100]

string底层是一个byte数组,因此string也可以进行切片处理

func main() {
	s := "itzhuzhu"
	fmt.Println(string(s[0]))
}

切片截取:

切片截取就是从切片中获取指定的数据返回给新切片,新切片与原切片会共用一个底层数组

如果初始化切片没有指定切片的容量,切片容量是跟随原切片的

切片截取的操作:

操作含义
s[n]从切片s的n索引到len(s)-1
s[:]从切片s的索引0到len(s)-1
s[low:]从切片s的索引low到len(s)-1
s[:high]从切片s的索引0到high,len=high
s[low:high]从切片s的索引low到high处所获得的切片,len=high-low
s[low:high:max]从切片s的索引low到high处所获得的切片,len=high-low,cap=max-low
len(s)切片s的长度,总是<=cap(s),如slice[:len(slice)]
cap(s)切片s的容量,总是>=len(s)

在这里插入图片描述

演示:

func main() {
	slice := []int{1, 2, 3, 4, 5}
	/**
		第一个值:截取的起始索引
		第二个值:截取的终止索引(不包括该值)
		第三个值:用来计算切片的容量,可以省略,默认和长度一样
		容量 = 第三个值 - 第一个值
		长度 = 第二个值 - 第一个值
	*/
	newSlice := slice[0:3:3]
	fmt.Println("newSlice:", newSlice, "长度:", len(newSlice), "容量:", cap(newSlice))

	// 和复制一样了
	newSlice2 := slice[:]
	fmt.Println("newSlice2:", newSlice2, "长度:", len(newSlice2), "容量:", cap(newSlice2))
}

输出:

newSlice: [1 2 3] 长度: 3 容量: 3
newSlice2: [1 2 3 4 5] 长度: 5 容量: 5
切片值的修改:

切片截取后返回新切片,对新切片的值进行修改,会影响原切片

演示:

func main() {
	slice := []int{1, 2, 3, 4, 5}
	newSlice2 := slice[0:3]
	fmt.Println("切片修改前slice的数据:", slice)
	newSlice2[0] = 1111
	fmt.Println("切片修改后slice的数据:", slice)
}

输出:

切片修改前slice的数据: [1 2 3 4 5]
切片修改后slice的数据: [1111 2 3 4 5]

原因:

切片截取后新的切片是指向了原来的切片,没有给新的切片开辟新的空间,所以对于新的切片操作会影响到原来的切片

新切片与原切片会共用一个底层数组

func main() {
   arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
   s := arr[2:5]
   // 没有指定切片的容量,则跟随原数组的容量
   fmt.Println("切片的长度:", len(s), "\t切片的容量:", cap(s))
   fmt.Println("s:", s)

   s2 := s[2:8]
   fmt.Println("s2:", s2)
   fmt.Println("切片的长度:", len(s2), "\t切片的容量:", cap(s2))
}

输出:

切片的长度: 3   切片的容量: 8
s: [3 4 5]
s2: [5 6 7 8 9 10]
切片的长度: 6   切片的容量: 6

切片作为函数参数:

切片可以做为函数的参数,但是在函数中修改切片的值,会影响到原切片

func main() {
	slice := make([]int, 10)
	SliceDemo(slice)
	fmt.Printf("main中slice的内存地址:%p\n数据:%v", slice, slice)
}

func SliceDemo(slice []int) {
	for i := 0; i < len(slice); i++ {
		slice[i] = i
	}
	fmt.Printf("SliceDemo中slice的内存地址:%p\n", slice)
}

输出:

SliceDemo中slice的内存地址:0x1400001e0f0
main中slice的内存地址:0x1400001e0f0
数据:[0 1 2 3 4 5 6 7 8 9]

使用append就不会影响,而且内存地址都不一样怎么会影响呢?因为append会变成一个新的切片

func main() {
	slice := []int{1, 2, 3, 4, 5}
	SliceDemo(slice)
	fmt.Println("main:", slice)
	fmt.Printf("main:%p", slice)
}

func SliceDemo(slice []int) {
	slice = append(slice, 6, 7)
	fmt.Println("SliceDemo:", slice)
	fmt.Printf("SliceDemo:%p\n", slice)
}

输出:

SliceDemo: [1 2 3 4 5 6 7]
SliceDemo:0x1400001c0a0
main: [1 2 3 4 5]
main:0x14000014180

切片求和:

func main() {
	// 定义变量,并收集用户输入的个数
	var count int
	fmt.Println("请输入要求和的个数:")
	fmt.Scan(&count)

	// 定义切片,将输入的个数保存到切片,count作为切片的长度
	slice := make([]int, count)

	// 统计求和数据
	statisticalData(slice)

	// 求和
	summation(slice)
}

func statisticalData(slice []int) {
	for i := 0; i < len(slice); i++ {
		fmt.Printf("请输入第%d个数\n", i+1)
		fmt.Scan(&slice[i])
	}
}

func summation(slice []int) {
	var sum int
	for i := 0; i < len(slice); i++ {
		sum += slice[i]
	}
	fmt.Println("和为:", sum)
}

切片求最大值:

func main() {
	// 定义变量,并收集用户输入的个数
	var count int
	fmt.Println("请输入要比较的个数:")
	fmt.Scan(&count)

	// 定义切片,将输入的个数保存到切片
	slice := make([]int, count)
	statisticalData(slice)

	// 比较最大值
	maximum(slice)
}

func statisticalData(slice []int) {
	for i := 0; i < len(slice); i++ {
		fmt.Printf("\n请输入第%d个数\n", i+1)
		fmt.Scan(&slice[i])
	}
}

func maximum(slice []int) {
	max := slice[0]
	for i := 0; i < len(slice); i++ {
		if max < slice[i] {
			max = slice[i]
		}
	}
	fmt.Println("\n最大值是:", max)
}
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzhuzhu.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值