Loading

【go语言学习】切片slice

go语言中数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。
go提供了一种更加灵活强悍的内置类型切片(“动态数组”)。
切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
切片是一个引用类型,它的内部结构包含地址长度容量

一、切片的声明和初始化

1、直接声明切片

var name []T
其中:name-切片的名称
           T-切片的类型

package main

import (
	"fmt"
)

func main() {
	var numSlice = []int{1, 3, 5, 7, 9}
	fmt.Println(numSlice)
	var stringSlice []string
	stringSlice = []string{"北京", "上海", "深圳", "苏州"}
	fmt.Println(stringSlice)
}

运行结果

[1 3 5 7 9]
[北京 上海 深圳 苏州]
2、基于数组创建切片

var slice = arr[low: high]
其中:表达式中的low和high表示一个索引范围(左包含,右不包含)

package main

import (
	"fmt"
)

func main() {
	var numArry = [5]int{1, 2, 3, 4, 5}
	var numSlice = numArry[2:4]
	fmt.Println(numSlice)
}

运行结果

[3 4]
3、使用make()函数构造切片

var slice = make([]T, len, cap)
其中:T-切片的元素类型
           len-切片的长度
           cap-切片的容量

package main

import (
	"fmt"
)

func main() {
	var intSlice = make([]int, 2, 4)
	fmt.Println(intSlice)
	fmt.Println(len(intSlice))
	fmt.Println(cap(intSlice))
}

运行结果

[0 0]
2
4

二、切片的本质

切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)
举例:
数组 a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[0:5],相应示意图如下:

切片s2 := a[3:6],相应示意图如下:

对slice所做的任何修改都将反映在底层数组中。
当多个切片共享相同的底层数组时,每个切片对元素所做的更改将在数组中反映出来。

package main

import (
	"fmt"
)

func main() {
	arr := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
	fmt.Println("操作前:", arr)
	slice := arr[:5]
	fmt.Println("操作前:", slice)
	slice[0] = 100
	fmt.Println("操作后:", arr)
	fmt.Println("操作后:", slice)
}

运行结果

操作前: [0 1 2 3 4 5 6 7]
操作前: [0 1 2 3 4]
操作后: [100 1 2 3 4 5 6 7]
操作后: [100 1 2 3 4]

三、切片的操作

1、切片的遍历

切片的遍历方式和数组是一致的,支持索引遍历和for range遍历。

package main

import (
	"fmt"
)

func main() {
	slice := []int{0, 1, 2, 3, 4, 5, 6, 7}
	for i := 0; i < len(slice); i++ {
		fmt.Printf("[%v-%v]\t", i, slice[i])
	}
	fmt.Println("")
	for k, v := range slice {
		fmt.Printf("[%v-%v]\t", k, v)
	}
}

运行结果

[0-0]   [1-1]   [2-2]   [3-3]   [4-4]   [5-5]   [6-6]   [7-7]   
[0-0]   [1-1]   [2-2]   [3-3]   [4-4]   [5-5]   [6-6]   [7-7]
2、切片的复制拷贝
  • 直接赋值拷贝,浅拷贝
    拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容
package main

import (
	"fmt"
)

func main() {
	slice1 := []int{0, 1, 2, 3, 4, 5, 6, 7}
	slice2 := slice1
	slice2[0] = 100
	fmt.Println("slice1:", slice1)
	fmt.Println("slice2:", slice2)
}

运行结果

slice1: [100 1 2 3 4 5 6 7]
slice2: [100 1 2 3 4 5 6 7]
  • 使用copy()函数操作,深拷贝

copy(destSlice, srcSlice)
其中:destSlice-目标切片
           srcSlice-源切片

package main

import (
	"fmt"
)

func main() {
	slice1 := []int{0, 1, 2, 3, 4, 5, 6, 7}
	slice2 := make([]int, 5, 5)
	copy(slice2, slice1)
	fmt.Println("slice2:", slice2)
	slice2[0] = 100
	fmt.Println("slice1:", slice1)
	fmt.Println("slice2:", slice2)
}

运行结果

slice2: [0 1 2 3 4]
slice1: [0 1 2 3 4 5 6 7]
slice2: [100 1 2 3 4]
3、向切片中添加元素append

Go语言的内建函数append()可以为切片动态添加元素。 可以一次添加一个元素,可以添加多个元素,也可以添加另一个切片中的元素(后面加…)

slice = append(slice, num1, num2)
slice = append(slice, slice...)

  • append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。
  • 但当slice中没有剩余空间(即(cap-len) == 0)时,切片就会自动按照一定的策略进行“扩容”,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。
package main

import (
	"fmt"
)

func main() {
	arr := [5]int{0, 1, 2, 3, 4}
	slice := arr[:3]
	slice = append(slice, 100)
	fmt.Println("arr:", arr)
	fmt.Println("slice:", slice)
	fmt.Println("------------------")
	slice = append(slice, 200)
	fmt.Println("arr:", arr)
	fmt.Println("slice:", slice)
	fmt.Println("------------------")
	slice = append(slice, 300)
	fmt.Println("arr:", arr)
	fmt.Println("slice:", slice)
}

运行结果

arr: [0 1 2 100 4]
slice: [0 1 2 100]
------------------
arr: [0 1 2 100 200]
slice: [0 1 2 100 200]
------------------
arr: [0 1 2 100 200]
slice: [0 1 2 100 200 300]
4、从切片中删除元素

Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。

slice = append(slice[:index], slice[index+1:]...)

package main

import "fmt"

func main() {
	slice := []int{1, 2, 3, 4, 5}
	slice = append(slice[:3], slice[4:]...)
	fmt.Println(slice)
}

运行结果

[1 2 3 5]
5、注意事项
  • 切片不能进行比较,切片唯一合法的比较操作是和nil比较。
  • 判断切片是否是空的,要使用len(slice) == 0来判断,不应该使用slice == nil来判断
package main

import "fmt"

func main() {
	var s1 []int
	s2 := []int{}
	s3 := make([]int, 0, 0)
	fmt.Printf("s1: len = %v, cap = %v, isNil:%v\n", len(s1), cap(s1), s1 == nil)
	fmt.Printf("s2: len = %v, cap = %v, isNil:%v\n", len(s2), cap(s2), s2 == nil)
	fmt.Printf("s3: len = %v, cap = %v, isNil:%v\n", len(s3), cap(s3), s3 == nil)
}

运行结果

s1: len = 0, cap = 0, isNil:true
s2: len = 0, cap = 0, isNil:false
s3: len = 0, cap = 0, isNil:false
posted @ 2020-10-27 19:04  Every_dawn  阅读(50)  评论(0编辑  收藏  举报