1 | Golang:基础数据结构
三个常用的数据类型的大致实现,有点绕,但是很有收获!
切片 Slice
数据结构
在64位架构的机器上,一个切片需要24字节的内存:指针字段需要8字节,长度和容量字段分别需要8字节。
var slice []int
创建的数据结构如下:
使用
对新切片的长度与容量的计算规则如下:
对底层数组容量是k的切片
slice[i:j]
来说
- 长度:
j - i
- 容量:
k - i
1 | func showSliceLenAndCap() { |
运行结果为:
其实也很好理解,对slice[i:j]
来说,i
为新切片的第一个元素,在就切片中的数组下标,k
是原数组最大的容量,所以新切片的容量为 k - i
;对 j
来说,可以视为一个前闭后开区间,即 [i, j)
,也就是不包含原数组中的第 j
位,所以长度为 j - i
。
当多个切片基于同一个数组时,修改共享部分的数据,在其他切片中也能看见改变。
1 | func showSharedArrayBySlice() { |
运行结果为:
扩容
切片扩容使用 append,长度一定改变,但容量未必改变。
append 可能会导致底层数组的值被覆盖,表现为别的共享此底层数组的切片,发现内容被改变。(不一定会发生,前置条件为必须是共享的底层数组)
如何创建一个不共享底层数组的切片?
让新产生的切片的长度、容量相同,第一次 append 的时候,发现容量不够,会进行自动扩容,使用新的底层数组,这样新增的元素,就不会影响其他共享了数组的切片
length 在 1000 以下,扩容时 x2;以上则 x1.25。
下面是上述小点的代码表述:
1 | func showSliceAppend() { |
运行结果如下:
迭代
range 获取到的是一个元素副本。也就是说,range 的两个返回值中的 value 的地址在内存中只有一个。
映射 Map
关于 Golang 中 map 的实现,有篇非常透彻的文章。与 Java 中的 HashMap 实现类似,都采用拉链法,但一个很大的不同之处在于,一个桶只能放8个键值对,超过8个,会放入溢出桶。
在Go语言里,通过键来索引映射时,即便这个键不存在也总会返回一个值。在这种情况下,返回的是该值对应的类型的零值。
在函数间传递映射并不会制造出该映射的一个副本。实际上,当传递映射给一个函数,并对这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改。
1 | Golang:基础数据结构