Golang基础篇-slice

558 阅读3分钟

本文主要介绍Go语言中slice,主要从源码分析和常见的面试例题两个角度加深对slice的理解。文中如有描述不对或则不合理的地方,请各位大佬积极留言,我会每日及时查看并核查纠正

slice的创建

func printSlice(sli []int) {
	fmt.Printf("value=%v, len=%d, cap=%d\n", sli, len(sli), cap(sli))
}

func test() {
	var sli01 []int
	for i := 0; i < 6; i++ {
		// printSlice(sli01)
		sli01 = append(sli01, 2 * i + 1)
	}
	fmt.Println(sli01)  // [1, 3, 5, 7, 9, 11]

	sli02 := []int {2, 4, 6, 8}
	printSlice(sli02)  // value=[2, 4, 6, 8], len=4, cap=4

	sli03 := make([]int, 2)
	printSlice(sli03)  // value=[0, 0], len=2, cap=2

	sli04 := make([]int, 2, 4)
	printSlice(sli04)  // value=[0, 0], len=2, cap=4
}

slice的用法

  • 开始索引包含,结束索引不包含
func test0001() {
	arr := [5]int {0, 1, 2, 3, 4}
	slice01 := arr[1: 4]  // [1, 2, 3]
	slice02 := arr[2: ]  // [2, 3, 4]
	slice03 := arr[: 3]  // [0, 1, 2]
	slice04 := arr[ : ]  // [0, 1, 2, 3, 4]
	fmt.Println(slice01, slice02, slice03, slice04)  // [1 2 3] [2 3 4] [0 1 2] [0 1 2 3 4]
}

slicemakecopyappend

func printSlice(sli []int) {
	fmt.Printf("value=%v, len=%d, cap=%d\n", sli, len(sli), cap(sli))
}

func copySliceDemo01() {
	sli01 := []int {2, 4, 6, 8}
	sli02 := make([]int, 6)
	printSlice(sli02)  // value=[0, 0, 0, 0, 0, 0], len=6, cap=6
	copy(sli02, sli01)
	printSlice(sli02)  // value=[2, 4, 6, 8, 0, 0], len=6, cap=6
	sli02 = append(sli02[:3], sli02[4:]...)
        /*
            slice02[ :3]底层结构:
                arr:                                 2    4    6    8    0    0
                slice02[ :3]                         2    4    6    空   空   空
                slice02[4: ]                                             0    0
                append(slice02[ :3], slice[4: ]...)  2    4    6    0    0   空
             
        */
	printSlice(sli02)  // value=[2, 4, 6, 0, 0], len=5, cap=6
}

slicearray之间的差别

slice本身没有数据,是对底层array的一个view

  • 数组一旦定义好,无法进行扩容等操作
  • 切片可以看成数组的一个view,数组右边容量为边界,当不越界数组的容量时候,以数组的容量视图进行计算,否则就会发生扩容
  • 一旦发生扩容,底层数组为新开辟的内存空间

func test0002() {
	arr := [5]int {0, 1, 2, 3, 4}
	fmt.Printf("arr=%x, len(arr)=%d, cap(arr)=%d\n", arr, len(arr), cap(arr))  // [0, 1, 2, 3, 4], 5, 5
	/*
		0  1  2  3  4  // len=5, cap=5
		   1  2  3 空  // len=3 cap=4
	*/
	slice01 := arr[1: 4]
	fmt.Printf("slice01=%x, len(slice01)=%d, cap(slice01)=%d\n", slice01, len(slice01), cap(slice01))  // [1, 2, 3], 3, 4
	/*
		0  1  2  3  4  // len=5, cap=5
		      2  3  4  // len=3, cap=3
	*/
	slice02 := arr[2: ]
	fmt.Printf("slice02=%x, len(slice02)=%d, cap(slice02)=%d\n", slice02, len(slice02), cap(slice02))  // [2, 3, 4], 3, 3
	/*
		0  1  2  3  4  // len=5, cap=5
		0  1  2 空  空  // len=3, cap=5
	*/
	slice03 := arr[ :3]
	fmt.Printf("slice03=%x, len(slice03)=%d, cap(slice03)=%d\n", slice03, len(slice03), cap(slice03))  // [1, 2, 3], 3, 5
	slice04 := arr[ : ]
	/*
		0 1 2 3 4  // len=5, cap=5
		0 1 2 3 4  // len=5, cap=5
	*/
	fmt.Printf("slice04=%x, len(slice04)=%d, cap(slice04)=%d\n", slice04, len(slice04), cap(slice04))  // [0, 1, 2, 3, 4], 5, 5

	fmt.Println("================================")
	/*
		0  1  2  3  4 			  // len=5, cap=5 数组没有扩容概念的
  		      2  3  4			  // len=3, cap=3 append前
		      2  3  4  5  空  空  // len=4, cap=6  append后 按照cap*2进行扩容, 当append前的cap>=1024,按照cap*0.25进行扩容
	*/
	slice05 = append(slice02, 5)
	fmt.Printf("arr=%x, len(arr)=%d, cap(arr)=%d\n", arr, len(arr), cap(arr))  // [0, 1, 2, 3, 4], 5, 5
	fmt.Printf("slice05=%x, len(slice05)=%d, cap(slice05)=%d\n", slice05, len(slice05), cap(slice05))  // [2, 3, 4, 5], 3, 3
}
	/*
		0  1  2  3  4 			    // len=5, cap=5 数组没有扩容概念的
		   1  2  3 空			     // len=3, cap=4 append前
		   1  2  3  6  7  空  空  空  // len=5, cap=8  append后 按照cap*2进行扩容, 当append前的cap>=1024,按照cap*0.25进行扩容
	*/
	slice06 := append(slice01, 6, 7)
	fmt.Printf("arr=%x, len(arr)=%d, cap(arr)=%d\n", arr, len(arr), cap(arr))  // [0, 1, 2, 3, 4], 5, 5
	fmt.Printf("slice06=%x, len(slice06)=%d, cap(slice06)=%d\n", slice06, len(slice06), cap(slice06))  // [1, 2, 3, 6, 7], 5, 8

slice源码分析

// slice底层实现是维护了三个属性,len/cap属性对应的值是值类型;array属性对应的值是指针类型,指向的是底层的数组Array
type slice struct {
	array unsafe.Pointer  // 该属性(array,下面以ptr代替,防止和数组冲突)对应的值是指向底层数组的内存地址
	len int  // slice的长度
	cap int  // slice的容量
}

slice01arr的关系

slice05arr的关系

小测试01

// slice类型传递
func modifySliceZeroIndexData(sliceVariable []int) {
	sliceVariable[0] = 1000
}

// slice可以向后扩展,但不允许向前扩展, slice[i]不可以向后扩展
func secondModifySlice(sliceVariable []int) {
	fmt.Println("-------------relice demo----------------")
	fmt.Println("reslice before--->", sliceVariable, cap(sliceVariable), len(sliceVariable))  // [1000, 2, 3], 4, 3
	reslice01 := sliceVariable[1: ]
	fmt.Println("reslice [1: ] after--->", reslice01, cap(reslice01), len(reslice01))  // [2, 3], 3, 2
	reslice02 := reslice01[1: 2]
	fmt.Println("reslice [1: 2] again after--->", reslice02, cap(reslice02), len(reslice02))  // [3], 2, 1
}

func sliceDemo01() {
	arr := [5]int {0, 1, 2, 3, 4}
	slice01 := arr[1: 4]  // [1, 2, 3]
	slice02 := arr[2: ]  // [2, 3, 4]
	slice03 := arr[: 3]  // [0, 1, 2]
	slice04 := arr[ : ]  // [0, 1, 2, 3, 4]
	fmt.Println(slice01, slice02, slice03, slice04)  // [1 2 3] [2 3 4] [0 1 2] [0 1 2 3 4]

	// 更新
	fmt.Println("update slice before slice value=>", slice01)  // [1, 2, 3]
	fmt.Println("update slice before arr value =>", arr)  // [0, 1, 2, 3, 4]
	modifySliceZeroIndexData(slice01)
	fmt.Println("update slice after slice value=>", slice01)  //  [1000, 2, 3]
	fmt.Println("update slice after arr value>", arr)  // [0, 1000, 2, 3, 4]

	// reslice
	secondModifySlice(slice01)
}

小测试02

for-range这边文章的最后一个期望之外的现象