GO数组与切片

定义:
数组是值类型,是一组同类型数据的集合,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。

切片的长度是不固定的,可以追加元素,数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型Slices切片(“动态数组"),在追加时可能使切片的容量增大。切片中有两个概念:一是len长度,二是cap容量,长度是指已经被赋过值的最大下标+1,可通过内置函数len()获得。容量是指切片目前可容纳的最多元素个数,可通过内置函数cap()获得。切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。

数据与切片的区别:
一、数据是固定长度,切片是可变长度
二、数据是值传递(这点是和其它语言不一样的地方),切片是地址传递。这也是经常在go语言代码中看到的传递数组时要加上&,而在接收函数中要加上*,以这种方式把数据变为地址传递,用切片也可以达到同样的效果。

切片的存在形态:
切片本身是无值的,他是对数组底层数据的镜像,所以把切片传入到被调用函数中后,被调用函数中的数据改了,数据的值也会跟着变。
另外还可以对切片进行reslice,但是不管reslice多少次,这些slice都是对底层数据的镜像,本身都是无值的。

切片取值超出了数组的边界怎么办?介绍一下slice的扩展
先举个例子:
arr := […]int{0,1,2,3,4,5,6,7}
// arr[2:6]的取值是{2,3,4,5} , 几乎数据语言的取值下标都是半开半闭区间的,即本例中包函下标2但不包函下标6的元素
slice1 := arr[2:6]
//slice2等于是从{2,3,4,5}中,从下标3取到下标5,但这里有个问题,slice1中并没有下标为4的元素,这里我们能取到吗? 答案是可以的,切片可以向后扩展取值,但是不可以向前扩展
slice2 := slice1[3,5]
//可以向后扩展不可以向前扩展的意思在哪呢?先看下图:
在这里插入图片描述
也就是说,slice其实是有三个参数值来定义的,ptr是指slice在数据中的超始位置,len是指slice向后取多少位,cap是指从超始位置到数据最后一个数据的长度。从这张图可以看出slice1[4]是存在于灰色部分,是可以向后扩展到的,所以仍然可以取到值。下图具体说明上面代码取值的内存结构
在这里插入图片描述
可以这样打印出数据的相应参数:
fmt.printf(“slice1=%v , len(slice1) = %d, cap(slice1)= %d”,s1,len(slice1),cap(slice1))

向slice中添加元素
arr2 = append(slice1,10)
fmt.printf(arr1,slice1)
这里会发现向slice1中添加元素后,slice1底层的数组并未改变,但是slice1的值却变了,按照我们上面说的,arr1是slice1底层数据,那么显然在append后,slice1底层的数据就变了,变成了arr2.
总结:

  1. 向slice中添加元素时,若元素个数超越了底层数据的长度,那么系统会重新为slice分配底层数组

     arr1 := [...]int{0,1,2,3,4,5}
     slice1 := arr1[2:6]
     arr2 := append(slice1,6)
     fmt.Println(arr1,slice1,arr2)
    

输出结果显示arr1中的值未改变,且分配了新的数组arr2:

[0 1 2 3 4 5] [2 3 4 5] [2 3 4 5 6]
  1. 向slice中添加元素时,若元素个数未超越了底层数据的长度,那么系统会覆盖底层数据对应下标位置的数据,如:

     arr1 := [...]int{0,1,2,3,4,5}
     slice1 := arr1[2:5]
     arr2 := append(slice1,6)
     fmt.Println(arr1,slice1,arr2)
    

输出结果显示arr1中的值变了,且也分配了新的数组arr2:

[0 1 2 3 4 6] [2 3 4] [2 3 4 6]

非基于数组来定义slice
上面都是基于数据来定义的slice,下面看一下如何直接定义slice以及slice的另一些特性,先看一段代码:

package main

import "fmt"

func main() {
	var s []int
	printfSlice(s)
	for i :=0;i<5;i++{
		s = append(s,i)
		printfSlice(s)
	}
	fmt.Println(s)
}

func printfSlice(s []int)  {
	fmt.Println("s=%v , len(s)=%d , cap(s)=%d",s,len(s),cap(s))
}

输了结果:

s=%v , len(s)=%d , cap(s)=%d [] 0 0
s=%v , len(s)=%d , cap(s)=%d [0] 1 1
s=%v , len(s)=%d , cap(s)=%d [0 1] 2 2
s=%v , len(s)=%d , cap(s)=%d [0 1 2] 3 4
s=%v , len(s)=%d , cap(s)=%d [0 1 2 3] 4 4
s=%v , len(s)=%d , cap(s)=%d [0 1 2 3 4] 5 8
[0 1 2 3 4]

从输出结果可以看到以下几个结论:

  1. slice定义时就已经被分配置了空间,并且被赋与初始值0. go中没有null用nil来表示,但是nil是有空间的可操作的只不过是初始值
  2. cap的增长是自动的并且是默认2倍的增长空间,并且按上文所述超过了cap长度时会分配新的底层数组

make创建slice
先上代码

package main

import "fmt"

func main() {
	s1 := make([]int,3)
	s2 := make([]int,3,10)
	printfSlice(s1)
	printfSlice(s2)
}

func printfSlice(s []int)  {
	fmt.Println("s=%v , len(s)=%d , cap(s)=%d",s,len(s),cap(s))
}

输入结果:

s=%v , len(s)=%d , cap(s)=%d [0 0 0] 3 3
s=%v , len(s)=%d , cap(s)=%d [0 0 0] 3 10

结果分析:

  1. make可以指定创建的slice len,且cap默认等于len
  2. make可以指定cap的容量大小,如果你预期你的数据会比较大,可以把初始容量设置大一些,可以减少一些内存分配的过程

slice的常用操作
上代码

package main

import "fmt"

func main() {
	s := []int{0,1,2,3}
	fmt.Println("copy slice")
	s1 := make([]int,8)
	copy(s1,s)
	printfSlice(s1)

	fmt.Println("delete slice middle element")
	s2 := append(s1[:3],s1[4:]...)
	fmt.Println(s2)

	fmt.Println("delete slice left element")
	s3 := []int{0,1,2,3,4,5,6,7,8}
	s4 := s3[1:]
	printfSlice(s4)

	fmt.Println("delete slice right element")
	s5 := []int{0,1,2,3,4,5,6,7,8}
	s6 := s5[:len(s5)-1]
	printfSlice(s6)

}

func printfSlice(s []int)  {
	fmt.Println("s=%v , len(s)=%d , cap(s)=%d",s,len(s),cap(s))
}

输了结果:

copy slice
s=%v , len(s)=%d , cap(s)=%d [0 1 2 3 0 0 0 0] 8 8
delete slice middle element
[0 1 2 0 0 0 0]
delete slice left element
s=%v , len(s)=%d , cap(s)=%d [1 2 3 4 5 6 7 8] 8 8
delete slice right element
s=%v , len(s)=%d , cap(s)=%d [0 1 2 3 4 5 6 7] 8 9

结果中显示 ,delete left element时cap未变,但是delete right element时cap变了,目前我没想明白为什么会变,但是它确实有这个特性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值