5

go语言中的slice

 3 years ago
source link: https://zhuanlan.zhihu.com/p/363678692
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

go语言中的slice

反对资本主义,反对官僚,反对帝国主义

go语言中有两种“自带”的数据结构。一种是slice,一种是map。

当然,有人会说,还有array呢,还有chan呢。要想研究slice,就必须同时研究array,所以我们将两者放在一起考虑。而至于chan,重点不是数据结构,而是并发编程,那是另一个领域了。(或许以后我们会讲讲)

今天,我们就来说说slice和array。

slice的平凡操作,我们就不说了,只说slice的骚操作,一共有三种:

// 切片
b:=a[1:]
// 增添
d:=append(c,42)
// 复制
copy(e,f)

这些操作看起来平平无奇,但是有的时候会伤害到你。比如:

	s1 := []int{1, 2, 3, 4}
	s2 := s1[1:2]
	s2 = append(s2, 42)
	fmt.Printf("%v\n", s1) // [1 2 42 4]

s1竟然“神奇”的被改变了。

当然,其实没有什么神奇的。只要对go的slice的机制有精准而简洁的理解,你就会平静接受这个现实并且开始兴致勃勃的利用起它。


go语言中的切片其实是对数组的一个描述。多个切片可以共用一个数组。

我们可以这样描述一个slice:

type slice struct {
	array array
	base int
	len int
} // 真实的代码不是这样的,这只是一个比喻(还需要考虑如何抽象能使得写代码更方便,甚至要考虑slice是否在栈上)

我们来举个例子

	a := [...]int{1, 2, 3, 4}
	s := a[1:3] // s.base==1 s.len==2

这样,昂贵的操作,都被封装在array里面了,你的代码里传来传去的slice,不过是对array的一个限制和描述。

这样做的好处是高效,坏处是程序员的心智负担会加重一些。

永远将slice考虑成引用类型。

当然,slice和任何go的struct一样,可以传值,也可以传指针。但这只是庸俗的理解,因为这忽略了slice的特殊性:它永远是array的引用类型。

js或者Python的数组就是值类型,这当然很好,只是效率会有那么一点点降低。


我们来看js中的concat方法:

[1,2,3].concat([4,5,6])

concat方法连接两个数组并返回新的数组。

当然,go语言没有必要做的和其他的语言一模一样,但如果非要你实现concat,该如何做呢?

要点就是:我们要不能在原来的数组上append,而是要新建一个数组。

func Concat(s1, s2 []int) (r []int) {
	r = make([]int, len(s1)+len(s2))
	copy(r, s1)
	copy(r[len(s1):], s2)
	return r
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK