概念:
切片出现的原因也是因为数组的可 *** 作性不高。切片的长度是不固定的,可以追加数据,可以理解切片是一个动态数组,切片的底层是一个结构体切片类型(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)
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)