Go基础:009.切片

76 阅读3分钟

切片概述

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比,切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

切片是可索引的,并且可以由 len() 方法获取长度。

切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

定义切片

package main

import "fmt"

func main() {
	//1. 声明一个切片,此时为nil
	var s1 []int
	fmt.Println(s1)                                        //[]
	fmt.Println(s1 == nil)                                 //true
	fmt.Printf("%s的长度为%d,容量为%d\n", "s1", len(s1), cap(s1)) //s1的长度为0,容量为0

	//2. 使用make函数创建一个int类型,长度为2的切片
	s2 := make([]int, 2)                                   //make(类型,长度)
	fmt.Println(s2)                                        //[0 0]
	fmt.Println(s2 == nil)                                 //false
	fmt.Printf("%s的长度为%d,容量为%d\n", "s2", len(s2), cap(s2)) //s2的长度为2,容量为2

	//3. 声明一个切片,并初始化
	s3 := []int{1, 3, 5}
	fmt.Println(s3)                                        //[1 3 5]
	fmt.Println(s3 == nil)                                 //false
	fmt.Printf("%s的长度为%d,容量为%d\n", "s3", len(s3), cap(s3)) //s3的长度为3,容量为3

	//4. 使用make函数创建一个int类型,长度为4,容量为5
	s4 := make([]int, 4, 5)                                //make(类型,长度,容量)
	fmt.Println(s4)                                        //[0 0 0 0]
	fmt.Println(s4 == nil)                                 //false
	fmt.Printf("%s的长度为%d,容量为%d\n", "s4", len(s4), cap(s4)) //s4的长度为4,容量为5
}

创建切片

package main

import "fmt"

func main() {
	//1. make函数创建切片
	s1 := make([]int, 3, 10)
	fmt.Println(s1) //[0 0 0]

	//1. 从数组创建切片
	arr := [5]int{1, 3, 5, 7, 9}
	s2 := arr[:]
	s3 := arr[1:]
	s4 := arr[:3]
	s5 := arr[1:3]
	fmt.Println(s2) //[1 3 5 7 9]
	fmt.Println(s3) //[3 5 7 9]
	fmt.Println(s4) //[1 3 5]
	fmt.Println(s5) //[3 5]

	//3. 从切片创建切片
	s6 := s5[:]
	fmt.Println(s6) //[3 5]
}

append()函数

package main

import "fmt"

func main() {
	s1 := []string{"1", "2", "3", "4"}
	fmt.Printf("len(s1)=%d cap(s1)=%d\n", len(s1), cap(s1)) //len(s1)=4 cap(s1)=4

	//1. 追加一个元素
	s1 = append(s1, "5")
	fmt.Printf("len(s1)=%d cap(s1)=%d\n", len(s1), cap(s1)) //len(s1)=5 cap(s1)=8

	//2. 追加多个元素
	s1 = append(s1, "6", "7", "8")
	fmt.Printf("len(s1)=%d cap(s1)=%d\n", len(s1), cap(s1)) //len(s1)=8 cap(s1)=8

	//3. 追加一个切片
	s2 := []string{"1", "2", "3"}
	s1 = append(s1, s2...)
	fmt.Printf("len(s1)=%d cap(s1)=%d\n", len(s1), cap(s1)) //len(s1)=11 cap(s1)=16
}

append()追加元素,原来的底层数组容量不够时,go底层会把底层数组替换,是go语言的一套扩容策略。

数组转切片

package main

import "fmt"

func main() {
	a1 := [...]int{1, 2, 3}
	s1 := a1[:]
	fmt.Printf("a1类型:%T\n", a1) //a1类型:[3]int
	fmt.Printf("s1类型:%T\n", s1) //s1类型:[]int
}

切片的本质

  1. 切片的本质:切片的本质是一个框,框住了一块连续的内存;
  2. 切片属于引用类型,真正的数据都是保存在底层数组里的;
  3. 切片可以简单理解为是快捷方式,修改会互相影响;
  4. 判断一个切片是否为空,使用len(s) == 0 判断,不能使用 s==nil 判断;

切片的实现

slice 底层的数据结构是一个结构体,包括三个元素:

  1. array(Pointer) 是指向一个数组的指针
  2. len 是当前 slice 的长度
  3. cap 是当前 slice 的容量

切片的扩容

slice 的扩容是一个很智能的动作,当发生扩容时,slice 会有两个动作:

  1. 扩大 slice 内部的 cap 值(按照 2 倍或者 1.25 倍扩大)。
  2. 重新申请一个 array,把原数据 copy 到这个新 array 上,然后把 slice 的 array 指针指向这个申请的 array。