go 数组与切片

go 数组与切片

数组

数组是同一类型元素的集合,类似于python中的集合类型。但是Go 语言中不允许混合不同类型的元素,例如包含字符串和整数的数组。(当然,如果是 interface{} 类型数组,可以包含任意类型)

数组的声明

一个数组的表示形式为 [n]Tn 表示数组中元素的数量,T 代表每个元素的类型。元素的数量 n 也是该类型的一部分,在定义式,数组的大小就固定了,并且不能修改

func main() {
   // 数组的定义方式
   var a [3]int    // 只定义不赋值
   var a [3]int = [3]int{4, 5, 6}  // [3]int 是该数组的数据类型
   a := [3]int{4}   // [3] 3 指数组的大小,数组的长度不能大于数组的大小
   a := [30]int{28:1}  // 数组大小为30,在索引为28的位置插入数据1,其余值以值类型的 0 值填充
   var a = [...]int{3, 4, 5}  // 虽然使用... 初始化,但是长度也固定了,根据值得多少确定长度
   var a =[...]int{50:99}
   fmt.Printf("%T",a)   // [51]int
   // 使用[...] 只支持这一种写法
}

补充: 
	数字,字符串,布尔,数组  ----》值类型 (值有自己的0值, 数字是0,字符串:""  布尔:false  数组:元素类型的零值)
	切片和map  ---->引用类型   零值是 nil  (None:python中所有类型的空值都是None)

go 的值类型与引用类型

数组的修改与循环

func main() {
	// 数组的通过索引取值与修改值,但是不能超出索引取值范围
	var a [3]int = [3]int{4, 5, 6}
	fmt.Println(a)
	a[0] = 99  
	fmt.Println(a[0])  // 99
	// 基于索引的循环
	for i:=0;i<len(a);i++{
		fmt.Println(a[i])
	}
	for i:=len(a)-1;i>=0;i--{
		fmt.Println(a[i])
	}
    // 基于迭代的循环  range是一个关键字,可以返回一个值,一个值就是索引,可以返回两个值,两个值就是索引和值
	for i:=range a{
		fmt.Println(a[i])
	}
	for i,value:=range a{
		fmt.Println(i,value)
	}
    
}

多维数据

多维数据,简单点说就是数据套数据,跟python中的列表类型一样,可以无限嵌套,这里仅以2层为例

var a [3][4]int=[3][4]int{{3,3,3,3},{4,4,4},{5}}
fmt.Println(a)
// 循环(两层循环)
for _,value:=range a{
    for _,v:=range value{
        fmt.Println(v)
    }
}

切片

切片是由数组建立的一种方便、灵活且功能强大的包装,切片本身不拥有任何数据,它们只是对现有数组的应用(指针指向数组)

在这里插入图片描述

切片的定义

1. 定义一个空切片
var s []int    // 括号中不带任何东西就是切片内型
2. 基于数组,引用赋值
a:=[10]int{1,2,3,4,5,6,7,8,9,10}
var s []int
// 把数组从头到尾的引用给s两种写法
s=a[0:len(a)]        // 前闭后开区间
s=[:]    

fmt.Printf("s的类型是:%T,值是:%v",s,s) // s的类型是:[]int,值是:[1 2 3 4 5 6 7 8 9 10]
3. 定义切片并初始化
var s []int=[]int{2,3,4}
fmt.Println(s)
fmt.Println(len(s)) // 长度是3
fmt.Println(cap(s)) // 容量是3

切片的使用

使用也就是通过索引取值和改值

a:=[10]int{1,2,3,4,5,6,7,8,9,10}
var s []int
s=[:] 
fmt.Println(s[0])  // 1
s[0]=999

fmt.Println(s[100])  // 编译不会报错,但是执行会,因为超出了切片的长度
fmt.Println(s)   // [999 2 3 4 5 6 7 8 9 10]
fmt.Println(a)   // [999 2 3 4 5 6 7 8 9 10]

你会发现当切片修改值时,底层数组的值也会发生相应的变化,始终与切片保存一致

结论1: 切片的变化,会影响底层数组跟着变化,同理,底层数组变化也会影响到切片变化

切片的长度和容量

数组有长度,并且长度是不能改变的,切片也有长度,但是长度可以改变,并且切片有容量

a:=[10]int{1,2,3,4,5,6,7,8,9,10}
var s []int=a[0:3]  // 前闭后开区间
fmt.Println(s)  // [1,2,3]
// 切片的长度,指当前切片有多长
fmt.Println(len(s))   // 3
// 切片的容量 : 这个切片最多能存多少值,基于底层数组来的
fmt.Println(cap(s)) //cap 内置函数,计算切片的容量  10

但是需要注意的是,切片的容量并不是一定等于底层数组的长度

a:=[10]int{1,2,3,4,5,6,7,8,9,10}
var s []int=a[3:6]  // 前闭后开区间
fmt.Println(s)  // [4,5,6]
fmt.Println(len(s))   // 3
fmt.Println(cap(s))  // 7

相信到这,也能够看出来了,切片容量是底层数组决定的,容量大小取决于切片是从那个地方开始切取数组的,容量 = 数组长度 - 切取起点(索引值)

make函数

可以通过make函数来创建切片,make函数有三个参数

  1. 第一个是类型 还可以用来创建其他类型数据
  2. 第二个是切片的长度
  3. 第三个是切片的容量
var s []int=make([]int,3,4) // 创建并赋值,但是没有放进去值,默认 0 值,
fmt.Println(s)  // [0 0 0]
fmt.Println(len(s)) // 长度是3
fmt.Println(cap(s)) // 容量是4

追加切片

切片可以通过append内置函数添加值

var s =make([]int,3,4)
fmt.Println(s)     // [0 0 0]
fmt.Println(len(s))  //3
fmt.Println(cap(s))  //4
s=append(s,99)
fmt.Println(s)    // [0 0 0 99]
fmt.Println(len(s)) //4
fmt.Println(cap(s)) //4
// 当切片已经到了容量的最大值时,如果再追加,长度+1,容量基于原来的容量翻倍,自动扩容
s=append(s,88)
fmt.Println(s)
fmt.Println(len(s)) // 5
fmt.Println(cap(s)) // 8

那么问题来了,通过前面的学习我们知道,切片是基于底层数组的引用,追加切片后底层数组是如何变化的呢?

a:=[10]int{1,2,3,4,5,6,7,8,9,10}
s:=a[2:9]
fmt.Println(s)
// 切片变,底层数组会变
s[0]=666
fmt.Println(s)  //[666 4 5 6 7 8 9]
fmt.Println(a) //[1 2 666 4 5 6 7 8 9 10]
// 数组变化,切片也会变
a[8]=999
fmt.Println(a) //[1 2 666 4 5 6 7 8 999 10]
fmt.Println(s) //[666 4 5 6 7 8 999]

切片追加到临界状态,再追加,数组就不够了,又会发生什么变化呢(代码接上)?

s=append(s,222)
fmt.Println(s) // [666 4 5 6 7 8 999 222]
//底层数组?也会跟着变
fmt.Println(a) // [1 2 666 4 5 6 7 8 999 222]

可以看到,如果底层数组不够了,切片再追加,不会再影响原数组。

go语言会自动申请一个新的数组,大小为原来切片容量的2倍,并将原来切片的值,复制到新的数组上,此时切片就跟原数组脱离了,指针指向了新数组

s=append(s,333)
fmt.Println(s)  //[666 4 5 6 7 8 999 222 333]
fmt.Println(len(s)) //9
fmt.Println(cap(s)) //16

多维切片

数组中有多维数组,切片基于数组,同样也有多维切片

var s [][]int
// 定义并初始化
var s [][]int=[][]int{{1,1,1,1,1,1,1,1,1,1,1},{2,2,},{3,3,3,3}}
fmt.Println(s)
fmt.Println(len(s))  // 3 内层一个切片算一个值
fmt.Println(cap(s))  // 3 
// 通过 make 初始化
var s [][]int=make([][]int,3,4)
// 内层的切片,没有初始化,只要使用内层切片,内层所有的切片都要初始化
fmt.Println(s[0])  // [] 切片,这个切片没有初始化
// fmt.Println(s[0][0])  // 报错
s[0]=make([]int,5,6)  // 对内层切片初始化
s[0][0]=99
fmt.Println(s[0])  
fmt.Println(s[0][5]) // 切片越界,虽然容量是6,但是还没使用到,就不能取

//循环多维切片(基于索引,基于迭代)
var s [][]int=[][]int{{1,1},{2,2},{3,3,3,3}}
for i:=0;i<len(s);i++{
    for j:=0;j<len(s[i]);j++{
        fmt.Println(s[i][j])
    }
}
for _,v:=range s{
    for _,v1:=range v{
        fmt.Println(v1)
    }
}

copy 切片

把一个切片,复制到另一个切片上,使用情景如下(内存优化方案)

var a [10000]int=[10000]int{3,4,5}
fmt.Println(a)
s:=a[:3]
fmt.Println(s)
// 只要使用s,底层基于了一个很大的数组,内存占用就很高
// 时候,就可以把s copy到另一个基于小数组的切片上
s1:=make([]int,3,3)
fmt.Println(s1)  // [0 0 0]
copy(s1,s)  
fmt.Println(s1)  //[3 4 5] ,以后都用s1操作,节约了内存

但是也有个问题,两个切片一样长,可以copy,如果不一样长呢?

s1:=make([]int,5,5)
s2:=make([]int,2,2)
copy(s1,s)
copy(s2,s)
fmt.Println(s1)  // [3 4 5 0 0]
fmt.Println(s2)  // [3 4]

可以很明显的看出来,容量不够就只copy部分,容量超了就以 零 型数据填充

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go&Python

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值