栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Go语言切片一网打尽,别再和Java语法傻傻分不清楚

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Go语言切片一网打尽,别再和Java语法傻傻分不清楚

前言

我总想着搞清楚,什么样的技术文章才算是好的文章呢?因为写一篇今后自己还愿意阅读的文章并不容易,暂时只能以此为目标努力。

最近开始用Go刷一些题,遇到了一些切片相关的细节问题,这里做一些总结。切片的设计想法是由动态数组概念而来,为了开发者可以更加方便的使一个数据结构可以自动增加和减少。但是切片本身并不是动态数据或者数组指针。

切片的结构
type slice struct {
	array unsafe.Pointer	 // 一个指向底层数组的指针(切片中元素是存在这个指向的数组中)
	len int								 // 切片的长度:包含的元素个数
	cap int								 // 切片的容量,len <= cap,如果len == cap,则添加元素会触发切片的扩容
}

长度为3,容量为5的int切片的图示如下,此时切片数组中可访问的部分只有下标0,1,2,超过部分不能访问。

声明和初始化 nil切片

声明nil切片,声明之后就会初始化(默认会用nil初始化),此时slice == nil成立,用于描述一个不存在的切片。

var slice []int	// 此时 slice == nil 成立

空切片

声明并初始化空切片,表示一个空的集合,空切片指向地址不是nil。

slice := make([]int, 0) // 此时 slice == nil 不成立
slice := []int{}

无论是nil切片还是空切片在调用内置函数append、len和cap的效果都是一样的。

含有元素的切片

此时切片非空。

slice := []int{1, 2, 3, 4, 5} 	// 声明并初始化一个切片,len和cap都是5
slice := make([]int, 4)					// 声明并初始化一个切片,第二个参数表示切片的长度len和容量cap都为4
slice := make([]int, 4, 6) 			// 声明并初始化一个切片,第二个参数表示len,第三个表示cap
-----------------------------------------------------------
array := [4]int{1, 2, 3, 4}			
slice := array[1:2]							// 对于array[i:j]来说,新切片的len=j-i,且cap=k-i(这里k是原数组的大小)

测试上面第四种初始化切片的方法:

func main() {
	array := [...]int{1, 2, 3, 4}
	slice := array[1:2]
	fmt.Printf("%p %d %d %vn", &slice, len(slice), cap(slice), slice)
}

拷贝 使用 := 拷贝

注意:下面代码中newSlice切片是通过slice切片声明并初始化的,虽然两个切片打印的地址不同,但是切片的地址指针指向的数组是同一个。修改slice[0] = 100之后,newSlice[0]也变成100。这种规则适用于将切片作为参数传递给函数,在函数的内部使用的是传入切片的值拷贝(创建一块新的内存存放切片,但切片的地址指针指向的是同一个数组)

func main() {
	array := [...]int{1, 2, 3, 4}
	slice := array[1:2]
	newSlice := slice				// 拷贝,newSlice由一块新的内存存放slice切片信息
  
	fmt.Printf("%p %d %d %vn", &slice, len(slice), cap(slice), slice)
	fmt.Printf("%p %d %d %vn", &newSlice, len(newSlice), cap(newSlice), newSlice)
	slice[0] = 100
	fmt.Printf("%p %d %d %vn", &slice, len(slice), cap(slice), slice)
	fmt.Printf("%p %d %d %vn", &newSlice, len(newSlice), cap(newSlice), newSlice)
}

使用copy函数拷贝

copy函数的两个参数是两个切片(将第二个切片的值覆盖到第一个切片),二者地址指针指向两个不同的数组。

func main() {
	slice1 := []int{1, 2, 3, 4, 5}
	slice2 := []int{5, 4, 3}
	fmt.Printf("%d %d %p %vn", len(slice1), cap(slice1), &slice1, slice1)
	fmt.Printf("%d %d %p %vn", len(slice2), cap(slice2), &slice2, slice2)
	//copy(slice2, slice1) 				// 只会复制slice1的前3个元素到slice2中
	copy(slice1, slice2) 					// 只会复制slice2的3个元素到slice1的前3个位置
	fmt.Printf("%d %d %p %vn", len(slice1), cap(slice1), &slice1, slice1)
	//fmt.Printf("%d %d %p %vn", len(slice2), cap(slice2), &slice2, slice2)
	slice2[0] = 200			
	slice1[0] = 100
	fmt.Printf("%d %d %p %vn", len(slice1), cap(slice1), &slice1, slice1)
	fmt.Printf("%d %d %p %vn", len(slice2), cap(slice2), &slice2, slice2)
}

扩容

扩容使用append方法。

len == cap 时添加元素
func main() {
	slice := []int{1, 2, 3}			// 此时slice的len == cap == 3,append元素会触发扩容,扩容后切片地址指向新数组(具体扩容策略这里暂时不多深入)
	fmt.Printf("%p %d %d %vn", &slice, len(slice), cap(slice), slice)
	newSlice := append(slice, 1)
	fmt.Printf("%p %d %d %vn", &newSlice, len(newSlice), cap(newSlice), newSlice)
	slice[0] = 100
	fmt.Printf("%p %d %d %vn", &slice, len(slice), cap(slice), slice)
	fmt.Printf("%p %d %d %vn", &newSlice, len(newSlice), cap(newSlice), newSlice)
}

len < cap 时添加元素

此时调用append方法添加一个元素1并不会创建新的数组,但是1会去覆盖掉array[2]。

func main() {
	array := [4]int{1, 2, 3, 4}
	slice := array[0:2]
	fmt.Printf("%p %d %d %vn", &slice, len(slice), cap(slice), slice)
	newSlice := append(slice, 1)
	fmt.Printf("%p %d %d %vn", &newSlice, len(newSlice), cap(newSlice), newSlice)
	slice[0] = 100
	fmt.Printf("%p %d %d %vn", &slice, len(slice), cap(slice), slice)
	fmt.Printf("%p %d %d %vn", &newSlice, len(newSlice), cap(newSlice), newSlice)
  fmt.Println("array = ", array)
}

结束

快过年了,祝大家新年快乐,春招offer拿不停。

建了一个春秋招备战/内推/闲聊群,欢迎大家加入。

关注公众号【程序员白泽】,带你走近一个有点话痨的程序员/学生党。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/721223.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号