go切片知识点回顾

116 阅读3分钟

每天进步一点点!

一、概念

切片(Slice)是一个拥有相同类型元素的可变长度的序列

二、声明

(一) 只声明类型不初始化赋值,值为nil

func main() {
	// 声明切片类型
	var a []string              //声明一个字符串切片
	fmt.Println(a)              //[]
  fmt.Println(a == nil)       //true

(二) 声明类型并初始化

func main() {
	// 声明切片类型
	var b = []int{}             //声明一个整型切片并初始化
	var c = []bool{false, true} //声明一个布尔切片并初始化
 
	fmt.Println(b)              //[]
  fmt.Println(b == nil)       //false
  
	fmt.Println(c)              //[false true]
	fmt.Println(c == nil)       //false
	}

三、注意事项

(一) 切片是引用类型,不支持直接比较,只能和nil比较

func main() {
	// 声明切片类型
	var c = []bool{false, true} //声明一个布尔切片并初始化
	var d = []bool{false, true} //声明一个布尔切片并初始化
	// fmt.Println(c == d)   //切片是引用类型,不支持直接比较,只能和nil比较
}

(二) 切片的底层就是一个数组,所以我们可以基于数组通过切片表达式得到切片。

  1. 切片表达式中的low和high表示一个索引范围(左包含,右不包含);
  2. 也就是下面代码中从数组a中选出1<=索引值<4的元素组成切片s;
  3. 得到的切片长度=high-low;
  4. 容量等于得到的切片的底层数组的容量。
func main() {
	a := [5]int{1, 2, 3, 4, 5}
	s := a[1:3]  // s := a[low:high]
	fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
  //s:[2 3] len(s):2 cap(s):4
}

(三) 切片表达式注意

package main

import "fmt"

func main() {
    a := [6]int{1, 2, 3, 4, 5, 6}
    s := a[1:3] // s := a[low:high]
    fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
    //s:[2 3] len(s):2 cap(s):5

    s2 := s[3:4] // 索引的上限是cap(s)而不是len(s)
    fmt.Printf("s2:%v len(s2):%v cap(s2):%v\n", s2, len(s2), cap(s2))
    //s2:[5] len(s2):1 cap(s2):2
}

(四) 要检查切片是否为空,请始终使用len(s) == 0来判断,而不应该使用s == nil来判断。因为只要初始化就不是nil,但是可能初始化一个空值,比如

func main() {
	// 声明切片类型
	var b = []int{}             //声明一个整型切片并初始化
 
	fmt.Println(b)              //[]
  fmt.Println(b == nil)       //false
	}

(五) 切片之间不能比较,切片只可以和nil比较

(六) 切片扩容源码,路径:C:\Program Files\Go\src\runtime\slice.go

newcap := oldCap
	doublecap := newcap + newcap
	if newLen > doublecap {
		newcap = newLen
	} else {
		const threshold = 256
		if oldCap < threshold {
			newcap = doublecap
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < newLen {
				// Transition from growing 2x for small slices
				// to growing 1.25x for large slices. This formula
				// gives a smooth-ish transition between the two.
				newcap += (newcap + 3*threshold) / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = newLen
			}
		}
	}

(七) 从切片中删除元素:a = append(a[:index], a[index+1:]...)

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]
}

(八) append方法为切片追加元素

1. 通过var声明的零值切片可以在append()函数直接使用,无需初始化。

因为 append 函数内部会处理底层数组的扩容和数据的复制。当我们向一个未初始化的 slice 使用 append 函数时,Go 编译器会自动为我们进行初始化操作。

var s []int
s = append(s, 1, 2, 3)

s := []int{}  // 没有必要初始化
s = append(s, 1, 2, 3)

var s = make([]int)  // 没有必要初始化
s = append(s, 1, 2, 3)
2. 但是,要用索引赋值必须初始化
package main

func main() {
    var c []int
    c[0] = 1000 //错误,panic: runtime error: index out of range [0] with length 0
}