golang的切片和数组

一、golang数组

数组的定义
var a [len]int,如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。

数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1,遍历数组一般有以下两种方式:

for i := 0; i < len(a); i++ {
}
for index, v := range a {
}

ps:数组支持比较,支持 “==”、"!=" 操作符,比较数组类型以及数组中的每一个元素,类型不同不能进行比较。

func main() {
   a := [5]int{1, 2, 3, 4}
   b := [5]int{1, 2, 3, 4}
   c := [5]int{1, 2, 3}
   
   fmt.Println(" a == b ", a == b)  //true
   fmt.Println(" a == c ", a == c)  //false

}

 

初始化数组
1、一维数组
var arr1 = [5]int{1, 2, 3, 4, 5} 指定元素个数

var arr2 = […]int{1, 2, 3, 4, 5, 6} 不指定元素个数 可以使用 … 代替数组的长度,编译器会根据元素个数自行推断数组的长度

var str = [5]string{3: “hello world”, 4: “tom”}
通过指定下标初始化元素:将索引为3和4的元素初始化。ps:初始化数组中 {} 中的元素个数不能大于 [] 中的数字。如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小。

a := [3]int{1, 2} 未初始化元素值为 0

b := […]int{1, 2, 3, 4} 通过初始化值确定数组长度

c := [5]int{2: 100, 4: 200} 使用索引号初始化元素

 
数组元素可以通过索引来读取(或修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
在这里插入图片描述
 
2、多维数组
var arr0 [5][3]int

var arr1 [2][3]int = […][3]int{{1, 2, 3}, {7, 8, 9}}

a := [2][3]int{{1, 2, 3}, {4, 5, 6}}

b := […][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 “…”

a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 /
{4, 5, 6, 7} , /
第二行索引为 1 /
{8, 9, 10, 11}, /
第三行索引为 2 */
}

func main() {
    var arr0 [5][3]int
    var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
 
    a := [3][4]int{       //a := [3][...]int{} 这样会报错
        {0, 1, 2, 3} ,
        {4, 5, 6, 7} ,
        {8, 9, 10, 11},
    }
 
    fmt.Println(arr0)
    fmt.Println(arr1)
    fmt.Println(a)
}

结果:
在这里插入图片描述
 
3、数组遍历

for index,val := range nums{
   
}


for i:=0;i<len(a);i++ {
   for j:=0;j<len(a[0]);j++ {
      fmt.Printf("(%d,%d)=%d ",i,j,a[i][j])
   }
}

结果:
在这里插入图片描述

二、golang切片
1、概念
切片和数组一样都是保存相同数组类型元素的容器,但切片的元素个数可变, 数组不可变,它是数组的引用,所以它是引用类型

2、创建切片

func main() {
    //1.声明切片
    var s1 []int
    if s1 == nil {
        fmt.Println("是空")
    } else {
        fmt.Println("不是空")
    }
 
    // 2.:=
    s2 := []int{}
 
    // 3.make()创建切片
    var s3 []int = make([]int, 0, 0)  //指定切片长度和容量
    // 简写 s3 := make([]int, 0)
    fmt.Println(s1, s2, s3)
 
    // 4.从数组切片
    arr := [5]int{1, 2, 3, 4, 5}
 
    // 前包后不包
    s6 := arr[1:4]
    fmt.Println(s6)
}

结果:
在这里插入图片描述

func main()  {
 
    slice1 := make([]string, 3, 10)
    slice2 := []string{"ZhangSan","LiSi","WangWu"}
 
    fmt.Printf("slice1切片长度: %d\n", len(slice1))
    fmt.Printf("slice1切片容量: %d\n", cap(slice1))
    fmt.Printf("slice2切片数据: %s\n", slice2)
 
}

结果:
在这里插入图片描述

3、切片的初始化
arr := […]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}

slice5 := arr[start:end]

slice6 := arr[:end]

slice7 := arr[start:]

切片常用操作和含义
在这里插入图片描述

func main()  {
    /* 创建切片 */
    numbers := []int{0,1,2,3,4,5,6,7,8}
 
    /* 打印原始切片 */
    fmt.Println("numbers ==", numbers)
 
    /* 打印子切片从索引1(包含) 到索引4(不包含)*/
    fmt.Println("numbers[1:4] ==", numbers[1:4])
 
    /* 默认下限为 0*/
    fmt.Println("numbers[:3] ==", numbers[:3])
 
    /* 默认上限为 len(s)*/
    fmt.Println("numbers[4:] ==", numbers[4:])
 
    /*读写操作实际目标是底层数组*/
    nums := numbers[2:4]
    nums[0] += 100
    nums[1] += 200
 
    /* 打印原始切片 */
    fmt.Println("numbers ==", numbers)
}

结果:
在这里插入图片描述

4、空(nil)切片
一个切片在未初始化之前默认为 nil,长度和容量为 0 且没有底层数组。

unc main()  {
    /* 创建切片 */
    var slice []int
    if(slice == nil){
        fmt.Printf("切片为空\n")
    }
    fmt.Printf("切片长度为: %d\n",len(slice))
}

结果:
在这里插入图片描述

5、append() 和 copy() 函数
如果要向切片中追加新元素,append 方法。如果要增加切片的容量,则必须创建一个新的更大的切片并把原切片的内容拷贝过来。

func main()  {
    /* 创建切片 */
    var slice1 []int
    slice1 = append(slice1,0)
    printMsg(slice1)
 
    slice1 = append(slice1,1,2,3)
    printMsg(slice1)
 
    /* 创建切片 slice2 是之前切片的两倍容量*/
    slice2 := make([]int, len(slice1), (cap(slice1))*2)
 
    /* 拷贝 slice1 的内容到 slice2 */
    copy(slice2, slice1)
    printMsg(slice2)
}
 
func printMsg(slice []int)  {
    fmt.Printf("len=%d cap=%d slice=%v\n",len(slice),cap(slice),slice)
}

结果:
在这里插入图片描述
超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满

func main() {
    data := [...]int{0, 1, 2, 3, 4, 10: 0}
    s := data[:2:3]  //s := data[low:high:max]   cap = max-low
    s = append(s, 100) //append 一个值,没超出 s.cap 限制。
 
    fmt.Println(s, data)        // 没有重新分配底层数组。
    fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。
 
 
    data1 := [...]int{0, 1, 2, 3, 4, 10: 0}
    s1 := data[:2:3]  //s := data[low:high:max]   cap = max-low
    s1 = append(s1, 100,200) //append 两个值,超出 s.cap 限制。
 
    fmt.Println(s1, data1)         // 重新分配底层数组,与原数组无关。
    fmt.Println(&s1[0], &data1[0]) // 比对底层数组起始指针。
}

结果:
在这里插入图片描述
ps:扩容通常以 2 倍容量重新分配底层数组,在大批量添加数据时,一次性分配足够大的空间比较好,以减少内存分配和数据复制开销。

 
6、切片的底层原理
切片底层包含3个字段:指向底层数组的指针、切片长度、切片允许增长到的元素的个数(容量),如下图。
在这里插入图片描述

基于同一个数组或切片创建的不同切片共享同一个底层数组。如果一个切片修改了该底层数组的共享部分,其他切片和原始数组或切片都能感知到。其底层数据结构如下面两个图所示:
在这里插入图片描述

func main() {
    data := []int{10, 20, 30, 40, 50}
 
    nums1 := data[:2]
    nums2 := data[2:]
    fmt.Println(data, nums1, nums2)
 
    nums1[1] = 200
    nums2[1] = 400
    fmt.Println(data, nums1, nums2)
}

结果:
在这里插入图片描述

7、切片与数组相比具有哪些优点
切片可以动态扩容,长度可变,数组长度不可变

Go 数组是值类型,赋值和函数传参操作都会复制整个数组数据。把第一个大数组传递给函数会消耗很多内存,采用切片的方式传参可以避免上述问题。切片是引用传递,所以它们不需要使用额外的内存并且比使用数组更有效率。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值