1 Go语言中切片 slice
vector

切片有些类似于其他语言中的数组,但是有一些不同寻常的特性。

因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。

1.1 切片的声明

两种声明切片类型的语句:
第一种

var sliceName []V
//其中 sliceName 是变量名
//V 表示切片中元素的类型

第二种

make()
func main() {
	// 声明切片类型
	var a []string //声明一个字符串切片
	var b = []int{} //声明一个整型切片并初始化
	//使用make()
	var slice2 []int = make([]int, 5, 7)  //长度为5 ,容器最大的容量是7的切片,变量名是slice2
}  

两种声明方法的对比

make()make()gcmake()

1.2 切片的长度和容量

len()cap()
由于切片的底层就是一个数组,所以我们可以基于数组定义切片。

注意:切片的截取是左闭右开的。(什么是左闭右开?见下c和d)

func main() {
	// 基于数组定义切片
	a := [5]int{1,2,3,4,5}
	//把b声明为从a种第一个元素到第四个元素的新切片。
	b := a[1:4] //基于数组a创建切片,包括元素a[1],a[2],a[3]
	fmt.Println(b) //[56 57 58]
	fmt.Printf("type of b:%T\n", b) //type of b:[]int
}
/* 还支持如下方式:
	c := a[1:] //[2 3 4 5]
	d := a[:4] //[1 2 3 4]
	e := a[:]  //[1 2 3 4 5]
*/
2 Go语言中切片类型出现的原因

切片是一种数据类型,这种数据类型便于使用和管理数据集合。
创建一个100万个int类型元素的数组,并将它传递给函数,将会发生什么?

一道面试真题
func main() {
    s := []int{1, 2, 3}                          
    ss := s[1:]                                        
    ss = append(ss, 4)

    for _, v := range ss {
        v += 10
    }
    for i := range ss {
        ss[i] += 10
    }
    fmt.Println(s) //
}

2.1 切片的结构

首先我们需要明白切片的结构。slice在Go的运行时库中就是一个C语言动态数组的实现。

type slice struct {
    ptr *array  //底层存储数组
    len int     //当前存储了多少个元素
    cap int     //底层数组可以存储多少个元素(从ptr指向的位置开始)
}

这个结构有3个字段,第一个字段表示array的指针,就是真实数据的指针(这个一定要注意),第二个是表示slice的长度,第三个是表示slice的容量,特别需要注意的是:

1.slice的长度和容量都不是指针
2.切片操作并不复制切片指向的元素。它创建一个新的切片并复用原来切片的底层数组。

切片操作并不复制切片指向的元素。它创建一个新的切片并复用原来切片的底层数组。 使得切片操作和数组索引一样高效。因此,通过一个新切片修改元素会影响到原始切片的对应元素。

切片的本质
a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}s1 := a[:5]
3 切片不能直接比较
nilnilnil0nil
var s1 []int         //len(s1)=0;cap(s1)=0;s1==nil
s2 := []int{}        //len(s2)=0;cap(s2)=0;s2!=nil
s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
len(s) == 0s == nil
4 切片的赋值拷贝
下面的代码中演示了拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。
func main() {
	s1 := make([]int, 3) //[0 0 0]
	s2 := s1             //将s1直接赋值给s2,s1和s2共用一个底层数组
	s2[0] = 100
	fmt.Println(s1) //[100 0 0]
	fmt.Println(s2) //[100 0 0]
}
5 切片遍历
for range
func main() {
	s := []int{1, 3, 5}

	for i := 0; i < len(s); i++ {
		fmt.Println(i, s[i])
	}

	for index, value := range s {
		fmt.Println(index, value)
	}
}
6 append()
append()append()append

举个例子:

func main() {
	//append()添加元素和切片扩容
	var numSlice []int
	for i := 0; i < 10; i++ {
		numSlice = append(numSlice, i)
		fmt.Printf("%v  len:%d  cap:%d  ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
	}
}
7 copy()

问题:

func main() {
	a := []int{1, 2, 3, 4, 5}
	b := a
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(b) //[1 2 3 4 5]
	b[0] = 1000
	fmt.Println(a) //[1000 2 3 4 5]
	fmt.Println(b) //[1000 2 3 4 5]
}
abba
copy()copy()

copy(destSlice, srcSlice []T)
// srcSlice: 数据来源切片
// destSlice: 目标切片

简单的例子:

func main() {
	// copy()复制切片
	a := []int{1, 2, 3, 4, 5}
	c := make([]int, 5, 5)
	copy(c, a)     //使用copy()函数将切片a中的元素复制到切片c
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(c) //[1 2 3 4 5]
	c[0] = 1000
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(c) //[1000 2 3 4 5]
}
8 删除元素

使用切片本身的特性来删除元素。

func main() {
	// 从切片中删除元素
	a := []int{30, 31, 32, 33, 34, 35, 36, 37}
	// 要删除索引为2的元素
	a = append(a[:2], a[3:]...)
	fmt.Println(a) //[30 31 33 34 35 36 37]
}
aindexa = append(a[:index], a[index+1:]...)