Golang——切片使用大全(创建、初始化、遍历、截取、修改、添加、切片的copy、切片作为函数参数、切片求和、切片求最大值)

Golang——切片使用大全(创建、初始化、遍历、截取、修改、添加、切片的copy、切片作为函数参数、切片求和、切片求最大值),第1张

概念:
切片出现的原因也是因为数组的可 *** 作性不高。切片的长度是不固定的,可以追加数据,可以理解切片是一个动态数组,切片的底层是一个结构体切片类型(slice)本身并不是动态数组或数组指针。它内部通过指针引用底层数组,设定相关属性将 *** 作限定在指定范围内。当需要时,会申请更大的内存,将当前数据复制过去, 以实现类似动态数组的功能。
type slice struct { 
	array unsafe.Pointer 
	len int 
	cap int 
}
切片的创建:

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

普通格式:

var 切片名 [] 数据类型

自动推导类型创建切片:

切片名 := [] 类型{}

make函数创建切片:
长度是已经初始化的空间,容量是已经开辟的空间,包括已经初始化的空间和空闲的空间

// 长度是不能大于容量的,容量可以省略不写,不写时候就默认和长度的值一样
切片名称 := make ([]切片类型,长度 容量)

// 返回切片的容量使用cap,返回切片的长度使用len
fmt.Println(cap(切片名称))

演示:

func main() {
	s1 := make([]int, 3, 5)    // 指定len、cap,底层数组初始化为零值
	s2 := make([]int, 3)       // 省略cap,和len相等
	s3 := []int{10, 20, 5: 30} // 按初始化元素分配底层数组,并设置len、cap,设置索引5的数据为30

	fmt.Println(s2, len(s2), cap(s2))
	fmt.Println(s1, len(s1), cap(s1))
	fmt.Println(s3, len(s3), cap(s3))
}

输出:

[0 0 0] 3 3
[0 0 0] 3 5
[10 20 0 0 0 30] 6 6
切片初始化:

三种创建格式,都是可以通过append向切片添加数据的

初始化格式:

/// 普通格式创建的切片
切片名 [索引] =// 自动推导类型创建的切片
切片名 := [] 类型{数据1,数据2,数据3}

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

演示:

func SliceDemo2() {
	var slice []int
	slice = append(slice, 1, 2, 3, 4)
	slice[1] = 111
	fmt.Println("切片中的数据", slice)
	fmt.Println("可以通过索引取部分数据", slice[0])
	fmt.Println("切片长度:", len(slice))
}

func SliceDemo03() {
	slice := []int{1, 2, 3, 4, 5}
	slice = append(slice, 6, 7, 8, 9, 10)
	slice[1] = 111
	fmt.Println("切片中的数据", slice)
	fmt.Println("可以通过索引取部分数据", slice[0])
	fmt.Println("切片长度:", len(slice))
}

func SliceDemo04() {
	slice := make([]int, 3, 4)
	for i := 0; i < len(slice); i++ {
		slice[i] = i + 1
	}
	
	slice = append(slice, 1)
	fmt.Println("切片中的数据", slice)
	fmt.Println("可以通过索引取部分数据", slice[0])
	fmt.Println("切片长度:", len(slice))
	fmt.Println("切片容量:", cap(slice))
}

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

func main() {
	var a []int
	b := []int{}
	println(a == nil, b == nil)
}

可获取元素地址,但不能向数组那样直接通过指针(*slice)访问元素内容

func main() {
	s := []int{0, 1, 2, 3, 4}
	p := &s     // 取s地址
	p0 := &s[0] // 取s[0]地址
	p1 := &s[1]
	println(p, p0, p1)

	// 取p中[1]的数据且加100
	(*p)[1] += 100 // *[]int不支持indexing *** 作,须先用指针 *** 作符获取[]int对象
	fmt.Println(s)
	fmt.Println((*p)[1]) //这里加括号是优先级问题
}

如果元素类型也是切片,那么就能实现类似交错数组的功能

func main() {
	x := [][]int{
		{1, 2},
		{10, 20, 30},
		{100},
	}
	fmt.Println(x[1])
	x[2] = append(x[2], 200, 300)
	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)
	}
}
切片截取:

切片截取就是从切片中获取指定的数据
如果初始化切片时,没有指定切片的容量,切片容量是跟随原切片的

切片截取的 *** 作:

*** 作含义
s[n]切片s中索引位置为n的项
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)
cap(s)切片s的容量,总是>=len(s)

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

	// 和复制一样了
	newSlice2 := slice[:]
	fmt.Println("截取后的切片", newSlice2)
	fmt.Println("切片的长度", len(newSlice2))
	fmt.Println("切片的容量", cap(newSlice2))
}
切片值的修改:

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

原因:

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

演示:

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]
append函数:

append函数是向切片的末尾slice(len)添加数据
如果添加的内容超出了切片初始定义的容量,切片会自动扩容
扩容机制是:上一次的容量 * 2
如果超过1024字节,每次扩容上一次的1/4
append每次扩容都是一个新的内存,和原来的无关联,所以如果是通过参数传递的方式,使用append添加数据,但是不会影响到原切片的数据,原因就是append每次拓展都是一个新的空间,指向的内存不再是原切片。

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

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

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

输出:

初始切片的数据: [0 0 0] 长度: 3
第一次添加数据: [0 0 0 1] 长度: 4
第二次添加数据: [0 0 0 1 2 3 4 5 6 7 8 9] 长度: 12
copy函数:

把切片2的数据(0索引到len-1)赋值到切片1中
注意:如果切片1的容量不够,则不赋值剩余的数据。如果切片1的数据比切片2的多,从切片2复制的数据是有多少,复制多少。
总结:copy只是复制索引相对应的数据,如果长度不够,不会覆盖原来的数据

格式:

copy(切片1,切片2)

演示:

 // 从切片2复制到切片1,但是切片2的数据比切片1的多,所以,最终只是复制了一部分,也就是索引相对应的数据
func main() {
	slice := []int{1, 2, 3}
	slice2 := []int{4, 5, 6, 7, 8, 9}
	copy(slice, slice2)
	fmt.Println(slice) // [4 5 6]
}

 // 从切片1复制到切片1,但是切片1的数据比切片2的少,所以,最终只是复制了一部分,也就是索引相对应的数据
func main() {
	slice := []int{1, 2, 3}
	slice2 := []int{4, 5, 6, 7, 8, 9}
	copy(slice2, slice)
	fmt.Println(slice2) // [1 2 3 7 8 9]
}

还可直接从字符串中复制数据到[ ]byte

func main() {
	b := make([]byte, 3)
	n := copy(b, "abcde")
	fmt.Println(n, b)
}
切片作为函数参数:

切片可以做为函数的参数,但是在函数中修改切片的值,会影响到原切片
因为切片的底层是结构体,结构体里有个参数Pointer,Pointer会指向切片的内存地址,使用的是浅拷贝方式,所以会影响到原切片值

演示:

func main() {
	slice := []int{1, 2, 3, 4, 5}
	SliceDemo10(slice)
}

func SliceDemo10(slice []int) {
	for _, v := range slice {
		fmt.Println(v)
	}
	slice = append(slice, 5, 6, 7)
	fmt.Println(slice)
}

输出:

1
2
3
4
5
[1 2 3 4 5 5 6 7]
切片求和:
func main() {
	// 定义变量,并收集用户输入的个数
	var count int
	fmt.Println("请输入要求和的个数:")
	fmt.Scan(&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("请输入第%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("最大值是:", max)
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/990065.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存