上一篇 Go圣经-临时插入ORM的小trick
数组
数组是一个固定长度类型的序列,由零个或者多个同构元素组成。Go语言很少直接使用数组,一般使用Slice多。数组的表现形式:
var q [3]int =[3]int{1,2,3} var r [3]int =[3]int{1,2} var s =[...]int{1,2,3}
数组类型是由类型和长度组成的。因此[3]int和[5]int是两个不同的数据类型
如果相同数组的元素类型是可以比较的,则这个数组是可以比较的。我们可以通过==进行数组比较。注意:这里不是C++,a==b,不是指针比较,而是数组所有元素的比较
。
我们如何比较两个动态数组是否相等呢?有两种方法:
- 第一种,比较slice的len、cap和底层数组元素
- 第二种,我们可以对slice序列化成byte流,然后利用sha256生成消息摘要, 摘要是32个字节的数组类型。然后直接比较即可。
func compareSlice(a []int, b []int) bool{ if len(a) != len(b) { return false } for i:=0 ;i<len(a); i++{ if a[i]!=b[i]{ return false } } return true }
这里有必要提一下,为啥判断a和b相等, 不比较cap(a)与cap(b)的大小关系呢?是不是比较时,只比较数据呢?
注意:不用比较slice的底层数组指针所指向的内存地址, 数据存放肯定不在一起的。
package main import ( "crypto/sha256" "fmt" ) func main() { c1 := sha256.Sum256([]byte("x")) c2 := sha256.Sum256([]byte("x")) fmt.Printf("%x\n%x\n%t\n%T\n", c1, c2, c1 == c2, c1) // Output: // 2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881 // 2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881 // true // [32]uint8 }
当调用函数时,调用参数将会被赋值给函数内部的参数变量,所以函数参数接收是一个变量的副本,并不是原始的变量。Go语言的数据传递全部是值类型。所以数组传递是低效的,并且对数组的修改都是发生在拷贝的数组上,对传入的实参没有任何影响。若是C++等其他语言,则数组传递时,是传递的指针。同时附带数组的长度信息。如果一定要传数组,请传数组指针,例如func(aptr *[32]byte)。
Slice动态数组
一个很形象的例子
months := []string{1: "January", /* ... */, 12: "December"}

这里有个我很懵逼的问题:
var months = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} // 夏天 summer:=months[6:9] fmt.Println(summer[:20]) // slice超出了cap(summer),直接panic。 明白,因为summer容量是12-6=6 fmt.Println(summer[:6]) // [ 7,8,9,10,11,12 ] ,这里有点懵逼,其实summer[:6]已经超出len(summer)了,我觉得应该是有越界控制的,但是没有 var months = make([]int, 0, 12) fmt.Println(months[:10]) // [ 0,0,0,0,0,0,0,0,0,0 ] months[0] = 1 // panic: index out of range
主要懵逼的问题:
- 当读取slice动态数组时,只要读取范围不超过cap容量就ok了
- 当写入slice动态数组时,只要超出len长度了,就报"index out of range"的panic
我的问题是,为什么不做一致性呢?读写都控制在len范围内呢?
利用slice动态数组,左旋转slice的前N个元素
package main import "fmt" func reverse(s []int) { for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } } func reverseN(s []int, n int) { reverse(s[:n]) // 反转前N个数 reverse(s[n:]) // 反转后len(s)-n-1 reverse(s) } func main() { var a = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} var n = 5 // 指定旋转前N个数 reverseN(a, n) fmt.Println(a) // [5 6 7 8 9 10 11 12 0 1 2 3 4] }
slice的比较问题
和数组不同的是,slice动态数组不能直接比较,不过标准库提供了bytes.Equal方法判断两个字节型slice是否相等。对于其他我们需要比较slice的元素个数和元素值。同时我们又引出struct类型的比较。
type Person struct { Cap int Len int Bt *byte } func main() { p1 := Person{ Cap: 28, Len: 28, Bt: new(byte), } p2 := Person{ Cap: 28, Len: 28, Bt: new(byte), } fmt.Println(p1 == p2) }
上面这个DEMO是可以编译通过,且运行没有问题的,返回false。
但是slice的底层结构和这个是类似的。但是它不能比较,slice是一个特殊类型。尽量少的考虑它是一个struct类型。同时struct能够进行比较,取决于内部的各个元素是否能够比较。
那为什么不直接slice动态数组比较呢?只需要比较元素个数和每个元素比较。我们从《Go圣经-学习笔记入门bufio》, 的bytes.ReadSlice方法就可以知道line可能是间接引用的,它底层的数据可能会随时发生变化。这样比较的话是没有意义的,所以安全的做法是直接禁止slice比较。