JAVA转go系列之 数组切片

数组对于编程语言来说都是比较重要的数据结构之一,数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。,java和go的数组也是差不多

java 利用new关键字来实例数组

dataType[] array= new dataType[arraySize]

上面的语句完成了两件事情,使用new关键字创建了一个dataType类型长度为arraySize的数组并且赋值给变量array

另外还可以使用如下方式来创建一个数组

dataType[] array = {dataTypeVal0,dataTypeVal1,dataTypeVal2,dataTypeVal3}

Go也大同小异,在Java中我们在编写程序的时候很少会用到数组,一般都是用集合来代替数组,在go中也是如此,go维护了一个slice(切片)来使数组使用的更加方便,

slice是对数组的抽象,go中的数组长度是固定的,在函数中传递的方式使用的是值的传递,用起来不是很方便,性能也会降低(值传递是copy一份来传递,copy会消耗掉性能)

定义切片

你可以使用make()函数来创建切片:

var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len) //这里的len是切片的初始长度
slice:=make([]int,5,10)

这样创建的切片长度是5,容量是10,这个容量10是对应的切片底层数组的

因为切片底层是数组,所以创建切片时如果不指定字面值的话,默认就是数组的元素的零值。这里我们指定的容量是10,但是我们只能访问5个元素,因为切片的长度是5,后面的5个元素,需要切片扩充后才能访问

容量必须>=长度,我们是不能创建长度大于容量的切片的。

还有一种创建切面的方式,使用字面量,就是指定初始化的值

这样的创建方式非常像数组,只是【】里面不需要声明长度,这样声明的切片长度和容量是相等的,并且会根据我们指定的字面量的值推断出来,当然我们也可以和数组一样,只初始化某个索引的值

这里指定了数组的长度是5个(从0开始)并且指定第5个元素的值是1其他元素的默认值都是0.顺便对比一下切片和数组的微小差别

输出结果

切片还有nil切片和空切片,他们的长度和容量都是0,但是他们指向底层数组的指针不一样,nil意味着指向底层数组的指针为nil,而空切片指针指向的是一个实际地址

//nil切片
var nilSlice []int
//空切片
slice:=[]int{}

nil切片表示一个不存在的切片,而空切片表示一个空的集合

切片的另外一个用处比较多的创建是基于现有的数组或者切片创建

slice := []int{1, 2, 3, 4, 5}
slice1 := slice[:]
slice2 := slice[0:]
slice3 := slice[:5]

fmt.Println(slice1)
fmt.Println(slice2)
fmt.Println(slice3)

基于现有的切片或者数组创建,使用【i:j】这样的操作符即可,它表示i索引开始,到j索引结束,截取原数组或者切片,创建一个新的切片。新切片的值包含原来切片i索引,但是不包括j的索引,对比java类似于string的substring方法,

i如果省略,默认为0,j如果省略,默认为原数组或切片的长度,示例中三个新切片的值都是一样的,这里的i和j都不能超过原切片的索引

这里需要注意一点的是,利用这种方式创建出来的新的切片和原来的切片是共用一个底层数组,也就是指正指向同一个地址,所以对新切片的值进行修改时原切片的值也会被修改。

使用切片

使用切片和数组一样,通过索引就可以获取切片对应元素的值,同样修改也对应

slice := []int{1, 2, 3, 4, 5}
fmt.Println(slice[2]) //获取值
slice[2] = 10 //修改值
fmt.Println(slice[2]) //输出10

切片实际上是一个动态的数组,它可以按需增长,我们使用内置的函数apperd即可。append可以为一个切片增加追加一个元素,至于如何增加,返回的是原来的切片还是一个新的切片,长度,容量如何改变这些细节,append函数都会自动帮我们处理。

append函数会智能的增长底层数组的容量,目前的算法是:容量小于1000个时,总是成倍的增长,一旦容量超过1000个,增长因子设为1.25,也就是说每次会增加百分之25的容量

内置的append是一个可变参数,所以我们可以同时追加好几个值

newSlice=append(newSlice,10,20,30)

此外我们还可以通过...操作符,把一个切片追加到另外一个切片里面

slice := []int{1, 2, 3, 4, 5}
newSlice := slice[1:2:3]

newSlice=append(newSlice,slice...)
fmt.Println(newSlice)
fmt.Println(slice)

迭代切片

切片是一个集合,我们可以使用for range循环来迭代它,

slice := []int{1, 2, 3, 4, 5}
	for i,v:=range slice{
		fmt.Printf("索引:%d,值:%d\n",i,v)
	}

 range返回的是切片元素的复制,而不是元素的引用。所以在for range里面对元素值的修改是不会影响到原切片的

在函数间传递切片

我们知道切片是3个字段构成的结构类型,所以函数之间以值的方式传递的时候,占用的成本非常小,成本很低。在传递复制切片的时候,其底层的数组不会被复制,也不会受影响,复制只是复制的切片本身,不涉及底层的数组

func main() {
	slice := []int{1, 2, 3, 4, 5}
	fmt.Printf("%p\n", &slice)
	modify(slice)
	fmt.Println(slice)
}

func modify(slice []int) {
	fmt.Printf("%p\n", &slice)
	slice[1] = 10
}

打印的输出如下:

0xc000046400
0xc000046440
[1 10 3 4 5]

仔细的看发现这两个地址不一样,所以确认切片在函数之间传递是复制的,而我们修改一个索引的值后,发现原切片的值也被修改了,说明他们共用一个底层数组

在函数之间传递切片是非常高效的,而且不需要传递指针和处理复杂的语法,只需要复制切片,然后根据自己的业务修改。最后传递回一个新的切片副本即可,这也是函数之间传递参数为甚么用使用切片而不是使用数组。

猜你喜欢

转载自blog.csdn.net/weixin_39982274/article/details/85159349