作业3 主题资源网站建设,qq小程序权限设置,小内存vps WordPress,男的做直播哪个网站切片的底层数据结构是数组#xff0c;所以#xff0c;切片是基于数组的上层封装#xff0c;使用数组的场景#xff0c;也完全可以使用切片。
类型比较
我看到 go 1.17 有对切片和数组转换的优化#xff0c;禁不住纳闷#xff0c;有什么场景是必须数组来完成的呢#x…切片的底层数据结构是数组所以切片是基于数组的上层封装使用数组的场景也完全可以使用切片。
类型比较
我看到 go 1.17 有对切片和数组转换的优化禁不住纳闷有什么场景是必须数组来完成的呢我特意去查了一下数组和切片的核心差异刨去有的没的核心就 2 点
数组是可比较的切片是不可比较的验证的方式也非常简单。声明一个 map 类型如果 key 的类型是切片会报如下的编译错误。如果是数组类型可以正常编译。数组赋值是值拷贝切片赋值是引用拷贝切片类型在值拷贝前后底层共用的还是同一个数组而数组的值拷贝就是值拷贝。 在查看的过程中我也有发现数组指针的用法除了可以通过数组本身来操作数组还可以通过数组指针来操作。而这种使用手法是切片不具备的。
下面的代码通过数组指针 (b) 可以像直接使用 b 一样访问数组的元素而通过使用指针赋值的方式也可以让两个变量共用同一个数组。
func main() {var b [3]int{11, 12, 13} //fmt.Println((b)[0])fmt.Println(b[0])
}下面数组的赋值过程 a 和 b 是值拷贝完全独立的两份数据b 数组修改第一个元素并不会影响到数组 a。而 c 和 a 是数组指针类型的值拷贝共享同一份数组数据c 数组修改第一个元素也就修改了 a 指向的数组。
func main() {var a [3]int{11, 12, 13} //b : ab[0] 10fmt.Println(a[0])fmt.Println(b[0])c : ac[0] 10fmt.Println(a[0])fmt.Println(c[0])
}类型转换
数组和切片的转换说实话在业务开发的过程中我基本上没有用到这种转换场景。因为切片的底层是数组数组可以直接转换为切片类型转换后切片和数组共享同一份空间。
func main() {var a [3]int{11, 12, 13} b : a[:len(a)]fmt.Printf(%T,%T, b, a)
}仔细想想切片在 go 中的结构为 SliceHeader 除了数组 Data 部分还包含了 Len 和 Cap 两个属性。所以go 数组转切片的过程也是封装这个 SliceHeader 的过程。
type SliceHeader struct {Data uintptrLen intCap int
}但在 1.17 之前切片是不能直接转换为数组的要想转换为数组就需要通过 unsafe 包来实现。不过切片要转换为数组相比数组转换为切片要简单很多。
原理上就是获取切片底层数组的第一个元素的指针转换为数组的指针。
func main() {var a []int{11, 12, 13} //b : (*[3]int)(unsafe.Pointer(a[0]))fmt.Println(b)
}现在 go 1.17 默认也支持了这个转换转换的方式也很好理解转换为数组类型的指针。因为是指针所以转换后的数组 b 和 a 仍然还是共用同一份数据。
为什么默认转换使用的类型是数组指针 (*[3]int)而非 ([3]int) 呢我理解如果是值类型的话还需要底层数据的拷贝语义上也没有指针类型好理解。
func main() {var a []int{11, 12, 13} //b : (*[3]int)(a)fmt.Println(b)
}应用
结合以往的工作比较适合使用数组替代切片的场景大概就是控制并发的顺序。
我们有一组数组 reqs要请求某一个接口。然后需要按照请求的顺序返回请求的结果。我们使用 go 协程并发请求接口然后基于请求的索引将接口返回的结果存储到对应索引数组的位置。
因为数组的个数是固定的所以这种场景还是比较少的 func main() {var reqs [3]interface{} [...]interface{}{1, 2, 3}var res [3]interface{}for i: range reqs {go func(index int) {res[index] reqs[index]}(i)}time.Sleep(time.Second)fmt.Println(res)
}