- Go语言切片(Slice)
- 切片是
可动态变化 的序列,是对数组的引用 ,引用类型 ,遵循引用传递的机制
- slice类型写作[ ]T,T是slice元素类型,
var s1 []int ,s1就是切片变量
package main
import "fmt"
func main() {
//创建一个数组
var array1 [5]int = [...]int{11, 22, 33, 44, 55}
/*
创建切片,通过对数组的索引切片
s1 是切片名
array1[1:3]代表slice引用数组区间,索引1到索引3的值,注意取头不取尾,
*/
s1 := array1[1:4]
fmt.Println(array1)
fmt.Println(s1)
fmt.Println(len(s1))
fmt.Println(cap(s1))
}
运行结果
[11 22 33 44 55] //原本数组
[22 33 44] //切片的值
3 //切片元素长度
4 //切片容量
2. 切片原理
slice是一个轻量级数据结构,提供访问数组子序列元素的功能。
slice由三个部分构成,指针、长度、容量
指针:指针指向slice第一个元素 对应的数组元素 的地址。
长度:slice元素的数量,不得超过容量。
容量:slice开始的位置 到底层数据的结尾 。
package main
import "fmt"
func main() {
//创建数组,Months月份,1月份到12月份
months:=[...]string{"","January","February","March","April","May","June","July","August","September","October","November","December"}
//创建切片,对数组的引用
s1:=months[4:7]//[April May June]
s2:=months[6:9]//[June July August]
fmt.Println(s1)
fmt.Println(s2)
//指针:指针指向slice`第一个元素`对应的`数组元素`的地址。
fmt.Printf("slice第一个元素地址%p\n",&s1[0])
fmt.Printf("对应数组元素的地址%p\n",&months[4])
}
对切片读写
package main
import (
"fmt"
)
func main() {
//创建数组data
data := [...]int{0, 1, 2, 3, 4, 5}
//切片s [2,3]
s := data[2:4]
//切片读写操作目标是底层数组data
s[0] += 100
s[1] += 200
fmt.Println(s)
fmt.Println(data)
}
运行结果
[102 203]
[0 1 102 203 4 5]
2.1. 创建切片的方式
- 定义切片,然后引用已经创建好的数组,数组可见
- 内置make函数创建切片,底层数组看不见,只能通过slice访问元素
make创建切片内存分配图
package main
import (
"fmt"
)
/*
内置make函数,参数(类型,len,cap),注意cap大于len,容量可以省略,默认等于长度
切片有默认值
*/
var slice0 []int = make([]int, 10)
var slice1 = make([]int, 10)
var slice2 = make([]int, 10, 10)
func main() {
fmt.Printf("make全局slice0 :%v\n", slice0)
fmt.Printf("make全局slice1 :%v\n", slice1)
fmt.Printf("make全局slice2 :%v\n", slice2)
fmt.Println("--------------------------------------")
slice3 := make([]int, 10)
slice4 := make([]int, 10)
slice5 := make([]int, 10, 10)
slice5[0] = 11
slice5[1] = 22
fmt.Printf("make局部slice3 :%v\n", slice3)
fmt.Printf("make局部slice4 :%v\n", slice4)
fmt.Printf("make局部slice5 :%v\n", slice5)
}
- 定义切片直接对应数组,如同make方式
package main
import "fmt"
func main() {
//第三种方式,原理类似make,数组看不见,由make维护
var s1 []int = []int{1, 2, 3, 4, 5}
fmt.Println(s1)
fmt.Println(len(s1))
fmt.Println(cap(s1))
}
4.遍历切片
package main
import "fmt"
func main() {
var arr [5]int = [...]int{11, 22, 33, 44, 55}
s1 := arr[1:4]
//for循环遍历
for i := 0; i < len(s1); i++ {
fmt.Printf("s1[%v]=%v\n", i, s1[i])
}
fmt.Println()
//for range方式遍历切片
for i, v := range s1 {
fmt.Printf("索引i=%v 值v=%v\n", i, v)
}
}
5.切片案例
package main
import "fmt"
func main() {
var array1 = [...]int{11, 22, 33, 44}
slice1 := array1[1:4] //11,22,33
slice2 := array1[1:] //22,33,44
slice3 := array1[:] //11,22,33,44
slice4 := slice3[:2] //slice4=[11,22] 切片再切片
fmt.Println(slice1)
fmt.Println(slice2)
fmt.Println(slice3)
fmt.Println(slice4)
}
6.cap是内置函数,统计切片容量,最大存放多少元素
7.切片扩容,append内置函数,向尾部添加数据,返回新的slice对象
package main
import "fmt"
func main() {
//创建切片
var slice1 []int = []int{100, 200, 300}
fmt.Printf("slice1容量=%v 长度=%v\n", cap(slice1), len(slice1))
//给切片追加新元素
//容量扩容机制是2倍扩容
slice1 = append(slice1, 400)
fmt.Printf("slice1扩容后容量=%v 长度=%v\n", cap(slice1), len(slice1))
fmt.Println(slice1)
//切片扩容切片,slice1... 语法糖代表展开切片元素
slice1=append(slice1,slice1...)
fmt.Println(slice1)
}
/*
append原理就是对底层数组扩容,go会创建新的数组,将原本元素拷贝到新的数组中
slice重新引用新的数组
这个数组不可见
*/
8.切片拷贝
package main
import "fmt"
func main() {
//创建切片
var slice1 []int = []int{11, 22, 33, 44}
//make创建切片,长度是10
var slice2 = make([]int, 10)
copy(slice2, slice1) //把slice1的值拷贝给slice2
fmt.Println(slice1) //[11 22 33 44]
fmt.Println(slice2) //[11 22 33 44 0 0 0 0 0 0]
//slice1和slice2数据独立,互不影响
slice1[0] = 123
fmt.Println(slice1)
fmt.Println(slice2)
}
9.全切片表达式
array[x:y:z]
x切片内容 [x:y]
Y切片长度: y-x
Z切片容量:z-x
package main
import (
"fmt"
)
//官网资料
// https://golang.google.cn/ref/spec#Slice_expressions
func main() {
//10:2代表索引10的元素是2
data := [...]int{0, 1, 2, 3, 4, 10: 2}
fmt.Println(data)
s := data[1:2:3] //data[start:end:数字-start] 这个s容量是3-1=2
fmt.Printf("扩容前s的容量是:%v\n", cap(s))
s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。
fmt.Println(s, data) // 重新分配底层数组,与原数组无关。
fmt.Printf("扩容后s的容量=%v\n", cap(s)) //二倍扩容
fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。
}
3. string和slice的联系
1)string底层就是byte数组,因此string同样可以进行切片处理
package main
import "fmt"
func main() {
str1 := "yugo niubi"
//对str1进行切片
s1 := str1[:4]
fmt.Println(s1)//yugo
}
2)string修改的两种方式
package main
import (
"fmt"
)
func main() {
str1 := "yugo niubi"
//string是不可变的,也无法通过切片修改值
//str1[0] = 's' 编译器失败
//修改string的方法,需要string转化为[]byte,修改后转为string
arr1 := []byte(str1) //类型强转
arr1[0] = 'g'
str1 = string(arr1)
fmt.Printf("str1=%v\n", str1)
//[]byte只能处理英文和数字,不能处理汉字,汉字3个字节,会出现乱码
//将string转为[]rune,按字符处理,兼容汉字
arr2 := []rune(str1)
arr2[0] = '于'
str1 = string(arr2)
fmt.Printf("str1=%v\n", str1)
}
|
请发表评论