Go 语言 切片的使用
一、切片的基本介绍:
-
切片
(slice)
是数组的一个引用,因此切片是引用类型。在传递时,遵守引用传递机制。 -
切片的使用和数组相似:
比如:遍历切片、访问切片的元素、求切片的长度等等;
-
切片是一个可以动态变化的数组。
-
切片定义的基本语法:
var 切片名 []类型
比如:var sliceName []int
定义切片名为sliceName 类型为int64
切片基本使用:
package main
import "fmt"
func main() {
// 切片的基本使用
intArr := [5]int{1, 2, 3, 4, 5}
// slice = intArr[1:3]
// 1.slice 就是切片名
// 2.intArr[1:3], 表示 slice 引用到了intArr这个数组
// 3.=引用intArr数组的起始下标为1, 结束下标为3(不包含结束下标) 左闭右开
slice := intArr[1:3]
fmt.Println("数组intArr元素为:", intArr)
fmt.Println("切片元素为:", slice)
fmt.Println("切片的长度为:", len(slice))
fmt.Println("切片的容量为:", cap(slice)) // 切片的容量是可以动态变化的
// intArr[1] 的地址 跟 slice[0]的地址相同
fmt.Printf("intArr[1]的地址:%p\n", &intArr[1])
fmt.Printf("slice[0]的地址:%p", &slice[0])
}
// 输出结果:
数组intArr元素为: [1 2 3 4 5]
切片元素为: [2 3]
切片的长度为: 2
切片的容量为: 4
intArr[1]的地址:0xc00000c308
slice[0]的地址:0xc00000c308
二、切片在内存中的布局:
以上代码,如图所示:
总结:
-
slice 是一个引用类型;
-
slice 就是一个数据结构**(struct结构体)**;
type slice struct {
ptr *[2]int // ptr 数据类型 数组
len int // len 数据类型 int
cap int
}
修改slice值,会影响到数组,因为slice是引用类型:
package main
import "fmt"
func main() {
// 切片的基本使用
intArr := [5]int{1, 2, 3, 4, 5}
slice := intArr[1:3]
slice[0] = 100
fmt.Println("intArr的数据为:", intArr)
}
// 输出结果:
intArr的数据为: [1 100 3 4 5]
三、切片的三种使用方式:
-
定义一个切片,然后让切片去引用一个已经创建好的数组:
package main import "fmt" func main() { // 切片的第一种使用方式, 引用数组 var arr [5]int = [...]int{1, 2, 3, 4, 5} var slice = arr[1:3] fmt.Println("arr:", arr) fmt.Println("slice:", slice) fmt.Println("slice的长度:", len(slice)) fmt.Println("slice的容量:", cap(slice)) } // 输出结果: arr: [1 2 3 4 5] slice: [2 3] slice的长度: 2 slice的容量: 4
-
通过make来创建切片:
基本语法:
var 切片名[]type = make([]type, len, cap)
参数说明:
-
type:数据类型;
-
len:指定大小;
-
cap:指定切片容量
若分配cap,需要 cap >= len
举个栗子:
package main import "fmt" func main() { // 切片定义的第二种方式, 通过make 来进行创建 var slice []float64 = make([]float64, 5, 10) slice[0] = 1 slice[3] = 2 fmt.Println("slice的值:", slice) fmt.Println("slice的长度:", len(slice)) fmt.Println("slice的容量:", cap(slice)) } // 输出结果: slice的值: [1 0 0 2 0] slice的长度: 5 slice的容量: 10
以上代码如图所示:
方式二总结:
-
通过make 创建的切片,可以指定大小、容量;
-
若没有给切片赋值,则使用默认值:
int float 类型为: 0
string 类型为:""
bool 类型为:false
-
通过make方式创建切片对应的数组是由make底层维护,对外不可见,只能通过slice去访问各个元素;
-
-
定义一个切片,直接指定具体数组,原理类似make:
package main import "fmt" func main() { // 直接定义一个切片 var strSlice []string = []string{"python", "golang", "java"} fmt.Println("strSlice的值:", strSlice) fmt.Println("长度:", len(strSlice)) fmt.Println("容量:", cap(strSlice)) } // 输出结果: strSlice的值: [python golang java] 长度: 3 容量: 3
1与2的区别
:
方式一:是直接引用数组,这个数组事先存在的,我们是可见的;
方式二:是通过make创建切片,make也会创建数组,是由切片在底层进行维护的;
四、切片遍历:
-
for 循环常规遍历:
package main import "fmt" func main() { // for 循环常规遍历: var arr = [...]int{10, 20 ,30 ,40, 50} slice := arr[1:4] for i := 0; i < len(slice); i++ { fmt.Printf("slice[%v]=%v\n", i, slice[i]) } } // slice[0]=20 slice[1]=30 slice[2]=40
-
for-range 循环遍历:
package main import "fmt" func main() { // for-range 循环遍历 mySlice := []int{10, 20, 30, 40, 50} for index, value := range mySlice{ fmt.Printf("index=%v;value=%v\n", index, value) } } // 输出结果: index=0;value=10 index=1;value=20 index=2;value=30 index=3;value=40 index=4;value=50
五、切片注意事项与使用细节:
-
切片初始化时需注意:
var slice = arr[startIndex: endIndex]
说明:arr数组下标为startIndex,取到下标为endIndex的元素(不含endIndex)
左闭右开
-
切片初始化时,不可越界。
可以动态增长
:var slice = arr[0:end] // 可以简写为: var slice = arr[:end]
var slice = arr[start:len(arr)] // 可以简写为: var slice = arr[start:]
var slice = arr[0:len(arr)] // 可以简写为: var slice = arr[:]
-
cap是一个内置函数,用于统计切片
容量
,最大可存放多少个元素: -
切片定义完后,不能使用,因为本身是
空的
,需要引用到一个数组,或者make一个空间给切片使用; -
切片可继续切片:
package main import "fmt" func main() { slice01 := []int{10, 20, 30, 40, 50} slice02 := slice01[1:4] // 20, 30, 40 fmt.Println("slice02的元素:", slice02) slice03 := slice02[:2] // 20, 30 fmt.Println("slice03的元素:", slice03) // slice01,slice02,slice03 指向的数据空间是一个 slice03[0] = 666 fmt.Println("修改后的slice03:", slice03) fmt.Println("查看之前slice01:", slice01) } // 输出结果: slice02的元素: [20 30 40] slice03的元素: [20 30] 修改后的slice03: [666 30] 查看之前slice01: [10 666 30 40 50]
六、内置函数append使用:
用append内置函数,可以对切片进行动态追加:
package main
import "fmt"
func main() {
// 用append 内置函数, 可以对切片进行动态追加
var slice []int = []int{10, 20, 30}
// 通过append 直接给slice 追加元素
newSlice := append(slice, 40, 50, 60)
fmt.Println("newSlice的值:", newSlice)
// 通过append 将切片slice 追加到 slice里面
slice = append(slice, slice...)
fmt.Println("slice的新值:", slice)
}
// 输出结果:
newSlice的值: [10 20 30 40 50 60]
slice的新值: [10 20 30 10 20 30]
- 切片append操作本质,就是对数组扩容;
- go 底层会创建一个新的数组;
- 新的数组是在底层维护的;
七、内置函数copy的使用:
切片使用copy内置函数完成拷贝:
package main
import "fmt"
func main() {
// 切片copy拷贝操作
slice := []int{1, 2, 3, 4, 5}
slice01 := make([]int, 10)
copy(slice01, slice)
fmt.Println("slice=", slice) // 1, 2, 3, 4, 5
fmt.Println("slice01=", slice01) // 1, 2, 3, 4, 5, 0, 0, 0, 0 ,0
}
// 输出结果:
slice= [1 2 3 4 5]
slice01= [1 2 3 4 5 0 0 0 0 0]
-
copy(params01, params02),参数的数据类型是切片;
-
slice 与 slice01 的数据空间是独立,相互不影响;
// 切片copy拷贝操作 slice := []int{1, 2, 3, 4, 5} slice01 := make([]int, 10) copy(slice01, slice) fmt.Println("slice=", slice) // 1, 2, 3, 4, 5 slice01[1] = 100 fmt.Println("slice01=", slice01) // 1, 100, 3, 4, 5, 0, 0, 0, 0 ,0
slice01 与 slice 是两个独立的空间, slice01的修改, 并不会影响到slice
八、string和slice 之间的注意事项:
-
string底层是一个byte数组,因此string 也可以进行切片操作:
package main import "fmt" func main() { // string 底层是一个byte数组, 因此string 也可以进行切片操作 myString := "python@java" // 将java取出 strJava := myString[7:] fmt.Println("strJava结果为:", strJava) } // 输出结果: strJava结果为: java
-
string是不可变的:
不能通过myString[0] = “go”
-
若要修改字符串,可通过将
string转换成[]byte,或者[]rune转换成string
:string 转换成[]byte(英文或数字即可):
package main import "fmt" func main() { // string 底层是一个byte数组, 因此string 也可以进行切片操作 myString := "python@java" // 将"python@java"改成golang@java byteArr := []byte(myString) byteArr[0] = 'g' byteArr[1] = 'o' byteArr[2] = 'l' byteArr[3] = 'a' byteArr[4] = 'n' byteArr[5] = 'g' myString = string(byteArr) fmt.Println("myString:", myString) } // 输出结果: myString: golang@java
[]rune转换成string(中文转换):
package main import "fmt" func main() { // []rune 转换成string myString := "您好北京!" runeString := []rune(myString) runeString[2] = '上' runeString[3] = '海' myString = string(runeString) fmt.Println("myString:", myString) } // 输出结果: myString: 您好上海!
细节:
- 转换成[]byte后,
可以处理英文和数字,但是不能处理中文
,原因是[]byte 是通过字节处理,而一个汉字,是3个字节,因此会出现乱码; - 将 string 转换成[]rune即可,因为[]rune是按字符处理,兼容汉字;
- 转换成[]byte后,