首页 > 其他分享 >【Go语言基础】slice

【Go语言基础】slice

时间:2023-02-21 09:11:39浏览次数:28  
标签:slice 语言 int s2 s1 Println Go fmt

一、概述

数组(Array)的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。

显然这种数据结构无法完全满足开发者的真实需求。Go语言提供了数组切片(slice)来弥补数组的不足。

切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。

slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。

二、基本语法

切片的创建和初始化

slice和数组的区别:声明数组时,方括号内写明了数组的长度或使用...自动计算长度,而声明slice时,方括号内没有任何字符。

var s1 []int //声明切片和声明array一样,只是少了长度,此为空(nil)切片
s2 := []int{}

//make([]T, length, capacity) //capacity省略,则和length的值相同
var s3 []int = make([]int, 0)
s4 := make([]int, 0, 0)

s5 := []int{1, 2, 3} //创建切片并初始化

注意:make只能创建slice、map和channel,并且返回一个有初始值(非零)。

// runtime/slice.go
type slice struct {
    array unsafe.Pointer // 元素指针
    len   int // 长度 
    cap   int // 容量
}

【引申1】[3]int 和 [4]int 是同一个类型吗?

不是。因为数组的长度是类型的一部分,这是与 slice 不同的一点。

cap和len的区别

简单点说,len(sli)表示可见元素有几个(即直接打印元素看到的元素个数),而cap(sli)表示所有元素有几个。

比如:

arr := []int{2, 3, 5, 7, 11, 13}
sli := arr[1:4]
fmt.Println(sli) //[3 5 7]
fmt.Println(len(sli))//3
fmt.Println(cap(sli))//5

三、切片的操作

切片截取

操作 含义
s[n] 切片s中索引位置为n的项
s[:] 从切片s的索引位置0到len(s)-1处所获得的切片
s[low:] 从切片s的索引位置low到len(s)-1处所获得的切片
s[:high] 从切片s的索引位置0到high处所获得的切片,len=high
s[low:high] 从切片s的索引位置low到high处所获得的切片,len=high-low
s[low:high:max] 从切片s的索引位置low到high处所获得的切片,len=high-low,cap=max-low
len(s) 切片s的长度,总是<=cap(s)
cap(s) 切片s的容量,总是>=len(s)

示例说明:

array := []int

操作 结果 len cap 说明
array[:6:8] [0 1 2 3 4 5] 6 8 省略 low
array[5:] [5 6 7 8 9] 5 5 省略 high、 max
array[:3] [0 1 2] 3 10 省略 high、 max
array[:] [0 1 2 3 4 5 6 7 8 9] 10 10 全部省略

append

append函数向 slice 尾部添加数据,返回新的 slice 对象:

var s1 []int //创建nil切换
//s1 := make([]int, 0)
s1 = append(s1, 1)       //追加1个元素
s1 = append(s1, 2, 3)    //追加2个元素
s1 = append(s1, 4, 5, 6) //追加3个元素
fmt.Println(s1)          //[1 2 3 4 5 6]

s2 := make([]int, 5)
s2 = append(s2, 6)
fmt.Println(s2) //[0 0 0 0 0 6]

s3 := []int{1, 2, 3}
s3 = append(s3, 4, 5)
fmt.Println(s3)//[1 2 3 4 5]

append函数会智能地底层数组的容量增长,一旦超过原底层数组容量,通常以2倍容量重新分配底层数组,并复制原来的数据:

func main() {
    s := make([]int, 0, 1)
    c := cap(s)
    for i := 0; i < 50; i++ {
        s = append(s, i)
        if n := cap(s); n > c {
            fmt.Printf("cap: %d -> %d\n", c, n)
            c = n
        }
    }
    /*
        cap: 1 -> 2
        cap: 2 -> 4
        cap: 4 -> 8
        cap: 8 -> 16
        cap: 16 -> 32
        cap: 32 -> 64
    */
}

copy

函数 copy 在两个 slice 间复制数据,复制⻓度以 len 小的为准,两个 slice 可指向同⼀底层数组。

data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := data[8:]  //{8, 9}
s2 := data[:5] //{0, 1, 2, 3, 4}
copy(s2, s1)    // dst:s2, src:s1

fmt.Println(s2)   //[8 9 2 3 4]
fmt.Println(data) //[8 9 2 3 4 5 6 7 8 9]

注意要目的方需要先分配好空间,不然会丢失。
比如 源有5个,目的3个,则只会拷贝3个过来。

g.textConfigsCopy = make([]string, len(g.textConfigs))
copy(g.textConfigsCopy, g.textConfigs)

切片删除元素

delete_index := 2
//查看删除位置之前的元素和 之后的元素
fmt.Println(slice[:delete_index], slice[delete_index+1:])
//将删除点前后的元素连接起来
slice = append(slice[:delete_index], slice[delete_index+1:]...)
fmt.Println(slice)

四、slice对底层数组的修改

package main

import "fmt"

func main() {
	slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	s1 := slice[2:5]
	fmt.Println(s1) //[2 3 20]

	s2 := s1[2:6:7] // 4,5,6,7
	s2 = append(s2, 100)
	s2 = append(s2, 200)
	fmt.Println(s2) //[4 5 6 7 100 200]

	s1[2] = 20//0, 1, 2, 3(赋值), 4, 5, 6, 7, 8, 9

	fmt.Println(slice) //[0 1 2 3 20 5 6 7 100 9]

//问题1:100怎么来的。
//问题2:为什么100后面是9不是200
}

打印一下看看

package main

import "fmt"

func main() {
	slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Println(len(slice), cap(slice)) //10 10

	s1 := slice[2:5]
	fmt.Println(s1) //[2 3 20]
	fmt.Println(len(s1), cap(s1)) //3 8

	s2 := s1[2:6:7] // 4,5,6,7
	fmt.Println(len(s2), cap(s2)) // 4 5

	s2 = append(s2, 100)
	s2 = append(s2, 200)
	fmt.Println(s2) //[4 5 6 7 100 200]

	//0, 1, 2, 3(赋值), 4, 5, 6, 7, 8, 9

	s1[2] = 20

	fmt.Println(slice) //[0 1 2 3 20 5 6 7 100 9]
}

100怎么来的:

100后面为什么是9?

s2扩容了。

标签:slice,语言,int,s2,s1,Println,Go,fmt
From: https://www.cnblogs.com/HappyTeemo/p/17139700.html

相关文章

  • 自动化测试如此容易!多语言自动化测试框架 Selenium 编程(C#篇)
    介绍Selenium官网:https://www.selenium.dev/Selenium是功能强大的自动化测试工具集,是支持Web浏览器自动化的一系列工具和库的总括项目,一共包括以下三个项目:Seleniu......
  • MongoDB使用记录
    查询publicList<CheckDataEntity>GetList(){Int64startTime=Stopwatch.GetTimestamp();//获取计时器机制中当前时间的最小单位数可以理解为时钟周期......
  • Django丨聚合与分组查询
    聚合查询聚合查询函数时对一组值执行计算,并返回单个值Django使用聚合查询前要先从django.db.models引用Avg、Max、Min、Count、Sum(首字母大写)fromdjango.db.modelsim......
  • 编译型还是解释型语言区分?开发语言类型的简单区分
    1.编译型语言和解释型语言1.1编译型语言定义:在程序运行之前,通过编译器将源程序编译成机器码(可运行的二进制代码),以后执行这个程序时,就不用再进行编译了。优点......
  • GoLang环境搭建
    goLangIDE选择VsCodeSDK下载首页-Go语言中文网-Golang中文社区(studygolang.com)  选择64位压缩包   下载后解压到磁盘目录 新建目录 C:\gol......
  • 大语言模型: 新的摩尔定律?
    译者按:最近一段时间,ChatGPT作为一个现象级应用迅速蹿红,也带动了对其背后的大语言模型(LLM)的讨论,这些讨论甚至出了AI技术圈,颇有些到了街谈巷议的程度。在AI技术圈......
  • 地理图形标记语言GML的架构及范例
    一、前言在信息高速公路的时代,如何能在不同的GIS系统间实时分享资料,是刻不容缓的问题,现在该问题已经有了一个很好解决案,那就是以XML为基础的GIS编码标准,称为地理图形标记......
  • Golang基础-闭包
    funcfib()func()int{ varn1,n2int returnfunc()int{ ifn1==0&&n2==0{ n1=1 }else{ n1,n2=n2,n1+n2 } returnn2 }}next......
  • C语言语法大纲
    ......
  • c语言程序填空项目
    1.头文件引用2变量的定义和初始化 3常用库函数的使用 4运算符和表达式的使用5实现顺序结构程序设计6用if语句switch语句实现分支结构的程序设计 7用whil......