• 6. Go的切片


    Go的切片

    为什么要使用切片

    切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。
    它非常灵活,支持自动扩容。

    切片是一个引用类型,它的内部结构包含地址、长度和容量。

    声明切片类型的基本语法如下:

    var name [] T
    
    • 1

    其中:

    • name:表示变量名
    • T:表示切片中的元素类型

    举例

    // 声明切片,把长度去除就是切片
    var slice = []int{1,2,3}
    fmt.Println(slice)
    
    • 1
    • 2
    • 3

    关于nil的认识

    当你声明了一个变量,但却还并没有赋值时,golang中会自动给你的变量赋值一个默认的零值。这是每种类型对应的零值。

    • bool:false
    • numbers:0
    • string:“”
    • pointers:nil
    • slices:nil
    • maps:nil
    • channels:nil
    • functions:nil

    nil表示空,也就是数组初始化的默认值就是nil

    var slice2 [] int
    fmt.Println(slice2 == nil)
    
    • 1
    • 2

    运行结果

    true
    
    • 1

    切片的遍历

    切片的遍历和数组是一样的

    var slice = []int{1,2,3}
    for i := 0; i < len(slice); i++ {
        fmt.Print(slice[i], " ")
    }
    
    • 1
    • 2
    • 3
    • 4

    基于数组定义切片

    由于切片的底层就是一个数组,所以我们可以基于数组来定义切片

    // 基于数组定义切片
    a := [5]int {55,56,57,58,59}
    // 获取数组所有值,返回的是一个切片
    b := a[:]
    // 从数组获取指定的切片
    c := a[1:4]
    // 获取 下标3之前的数据(不包括3)
    d := a[:3]
    // 获取下标3以后的数据(包括3)
    e := a[3:]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行结果

    [55 56 57 58 59]
    [55 56 57 58 59]
    [56 57 58]
    [55 56 57]
    [58 59]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    同理,我们不仅可以对数组进行切片,还可以切片在切片

    切片的长度和容量

    切片拥有自己的长度和容量,我们可以通过使用内置的len)函数求长度,使用内置的cap()
    函数求切片的容量。

    切片的长度就是它所包含的元素个数。

    切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。切片s的长度和容量可通过表达式len(s)和cap(s)来获取。

    举例

    // 长度和容量
    s := []int {2,3,5,7,11,13}
    fmt.Printf("长度%d 容量%d\n", len(s), cap(s))
    
    ss := s[2:]
    fmt.Printf("长度%d 容量%d\n", len(ss), cap(ss))
    
    sss := s[2:4]
    fmt.Printf("长度%d 容量%d\n", len(sss), cap(sss))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果

    长度6 容量6
    长度4 容量4
    长度2 容量4
    
    • 1
    • 2
    • 3

    为什么最后一个容量不一样呢,因为我们知道,经过切片后sss = [5, 7] 所以切片的长度为2,但是一因为容量是从2的位置一直到末尾,所以为4

    切片的本质

    切片的本质就是对底层数组的封装,它包含了三个信息

    • 底层数组的指针
    • 切片的长度(len)
    • 切片的容量(cap)

    举个例子,现在有一个数组 a := [8]int {0,1,2,3,4,5,6,7},切片 s1 := a[:5],相应示意图如下
    在这里插入图片描述
    切片 s2 := a[3:6],相应示意图如下:
    在这里插入图片描述

    使用make函数构造切片

    我们上面都是基于数组来创建切片的,如果需要动态的创建一个切片,我们就需要使用内置的make函数,格式如下:

    make ([]T, size, cap)
    
    • 1

    其中:

    • T:切片的元素类型
    • size:切片中元素的数量
    • cap:切片的容量

    举例:

    // make()函数创建切片
    fmt.Println()
    var slices = make([]int, 4, 8)
    //[0 0 0 0]
    fmt.Println(slices)
    // 长度:4, 容量8
    fmt.Printf("长度:%d, 容量%d", len(slices), cap(slices))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    需要注意的是,golang中没办法通过下标来给切片扩容,如果需要扩容,需要用到append

    slices2 := []int{1,2,3,4}
    slices2 = append(slices2, 5)
    fmt.Println(slices2)
    // 输出结果 [1 2 3 4 5]
    
    • 1
    • 2
    • 3
    • 4

    同时切片还可以将两个切片进行合并

    // 合并切片
    slices3 := []int{6,7,8}
    slices2 = append(slices2, slices3...)
    fmt.Println(slices2)
    // 输出结果  [1 2 3 4 5 6 7 8]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要注意的是,切片会有一个扩容操作,当元素存放不下的时候,会将原来的容量扩大两倍

    使用copy()函数复制切片

    前面我们知道,切片就是引用数据类型

    • 值类型:改变变量副本的时候,不会改变变量本身
    • 引用类型:改变变量副本值的时候,会改变变量本身的值

    如果我们需要改变切片的值,同时又不想影响到原来的切片,那么就需要用到copy函数

    // 需要复制的切片
    var slices4 = []int{1,2,3,4}
    // 使用make函数创建一个切片
    var slices5 = make([]int, len(slices4), len(slices4))
    // 拷贝切片的值
    copy(slices5, slices4)
    // 修改切片
    slices5[0] = 4
    fmt.Println(slices4)
    fmt.Println(slices5)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行结果为

    [1 2 3 4]
    [4 2 3 4]
    
    • 1
    • 2

    删除切片中的值

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

    // 删除切片中的值
    var slices6 = []int {0,1,2,3,4,5,6,7,8,9}
    // 删除下标为1的值
    slices6 = append(slices6[:1], slices6[2:]...)
    fmt.Println(slices6)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行结果

    [0 2 3 4 5 6 7 8 9]
    
    • 1

    切片的排序算法以及sort包

    编写一个简单的冒泡排序算法

    func main() {
    	var numSlice = []int{9,8,7,6,5,4}
    	for i := 0; i < len(numSlice); i++ {
    		flag := false
    		for j := 0; j < len(numSlice) - i - 1; j++ {
    			if numSlice[j] > numSlice[j+1] {
    				var temp = numSlice[j+1]
    				numSlice[j+1] = numSlice[j]
    				numSlice[j] = temp
    				flag = true
    			}
    		}
    		if !flag {
    			break
    		}
    	}
    	fmt.Println(numSlice)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在来一个选择排序

    // 编写选择排序
    var numSlice2 = []int{9,8,7,6,5,4}
    for i := 0; i < len(numSlice2); i++ {
        for j := i + 1; j < len(numSlice2); j++ {
            if numSlice2[i] > numSlice2[j] {
                var temp = numSlice2[i]
                numSlice2[i] = numSlice2[j]
                numSlice2[j] = temp
            }
        }
    }
    fmt.Println(numSlice2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    对于int、float64 和 string数组或是切片的排序,go分别提供了sort.Ints()、sort.Float64s() 和 sort.Strings()函数,默认都是从小到大进行排序

    var numSlice2 = []int{9,8,7,6,5,4}
    sort.Ints(numSlice2)
    fmt.Println(numSlice2)
    
    • 1
    • 2
    • 3

    降序排列

    Golang的sort包可以使用 sort.Reverse(slic e) 来调换slice.Interface.Less,也就是比较函数,所以int、float64 和 string的逆序排序函数可以这样写

    // 逆序排列
    var numSlice4 = []int{9,8,4,5,1,7}
    sort.Sort(sort.Reverse(sort.IntSlice(numSlice4)))
    fmt.Println(numSlice4)
    
    • 1
    • 2
    • 3
    • 4
    package main
    
    import "fmt"
    
    func main() {
    	// 声明切片,把长度去除就是切片
    	var slice = []int{1, 2, 3}
    	fmt.Println(slice)
    
    	var slice2 []int
    	fmt.Println(slice2 == nil)
    
    	for i := 0; i < len(slice); i++ {
    		fmt.Print(slice[i], " ")
    	}
    	fmt.Println()
    	// 基于数组定义切片
    	a := [5]int{55, 56, 57, 58, 59}
    	// 获取数组所有值,返回的是一个切片
    	b := a[:]
    	// 从数组获取指定的切片
    	c := a[1:4]
    	// 获取 下标3之前的数据(不包括3)
    	d := a[:3]
    	// 获取下标3以后的数据(包括3)
    	e := a[3:]
    
    	fmt.Println(a)
    	fmt.Println(b)
    	fmt.Println(c)
    	fmt.Println(d)
    	fmt.Println(e)
    
    	// 长度和容量
    	s := []int{2, 3, 5, 7, 11, 13}
    	fmt.Printf("长度%d 容量%d\n", len(s), cap(s))
    
    	ss := s[2:]
    	fmt.Printf("长度%d 容量%d\n", len(ss), cap(ss))
    
    	sss := s[2:4]
    	fmt.Printf("长度%d 容量%d\n", len(sss), cap(sss))
    
    	// make()函数创建切片
    	fmt.Println()
    	var slices = make([]int, 4, 8)
    	//[0 0 0 0]
    	fmt.Println(slices)
    	// 长度:4, 容量8
    	fmt.Printf("长度:%d, 容量%d \n", len(slices), cap(slices))
    
    	// 切片扩容
    	slices2 := []int{1, 2, 3, 4}
    	slices2 = append(slices2, 5)
    	fmt.Println(slices2)
    
    	// 合并切片
    	slices3 := []int{6, 7, 8}
    	slices2 = append(slices2, slices3...)
    	fmt.Println(slices2)
    
    	// copy函数,我们知道切片
    	var slices4 = []int{1, 2, 3, 4}
    	var slices5 = make([]int, len(slices4), len(slices4))
    	copy(slices5, slices4)
    	slices5[0] = 4
    	fmt.Println(slices4)
    	fmt.Println(slices5)
    
    	// 删除切片中的值
    	var slices6 = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    	// 删除下标为1的值
    	slices6 = append(slices6[:1], slices6[2:]...)
    	fmt.Println(slices6)
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    效果

    [1 2 3]
    true
    1 2 3 
    [55 56 57 58 59]
    [55 56 57 58 59]
    [56 57 58]
    [55 56 57]
    [58 59]
    长度6 容量6
    长度4 容量4
    长度2 容量4
    
    [0 0 0 0]
    长度:4, 容量8 
    [1 2 3 4 5]
    [1 2 3 4 5 6 7 8]
    [1 2 3 4]
    [4 2 3 4]
    [0 2 3 4 5 6 7 8 9]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    win10 Tesseract-ORC安装教程以及使用案例(pdf拆分)
    深度学习笔记(3)——pytorch+TextCNN实现情感分类(外卖数据集)
    跟着AI学AI_11 PyTorch, TensorFlow 和JAX 功能对比简介
    npm 常用的命令
    基于多模态知识图谱的多模态推理-MR-MKG
    我的创作纪念日
    @Autoweird和@Resourse的区别 java定义Bean的方式
    云汉芯城一站式电子制造平台启想智联顺利通过IATF16949:2016质量管理体系认证
    Vue.js+SpringBoot开发木马文件检测系统
    生物标志物发现中的无偏数据分析策略
  • 原文地址:https://blog.csdn.net/wangzhicheng987/article/details/126698929