使⽤append向Slice追加元素时, 如果Slice空间不⾜, 将会触发Slice扩容, 扩容实际上重新⼀配⼀块更⼤的内存, 将原Slice数据拷贝进新Slice, 然后返回新Slice, 扩容后再将数据追加进去。 扩容容量的选择遵循以下规则: 如果原Slice容量⼩于1024, 则新Slice容量将扩⼤为原来的2倍 如果原Slice容量⼤于等于1024, 则新Slice容量将扩⼤为原来的1.25倍 使⽤copy()内置函数拷贝两个切⽚,但是需要注意的是,copy 会将源切⽚的数据逐个拷贝到⽬的切⽚指向的数组中, 拷贝数量取两个切⽚长度的最⼩值。copy不会扩容,只有append才会扩容。 基于以上切⽚特性。编程过程需要注意: 创建切⽚时可跟据实际需要预分配容量, 尽量避免追加过程中扩容操作, 有利于提升性能; 切⽚拷贝时需要判断实际拷贝的元素个数 谨慎使⽤多个切⽚操作同⼀个数组, 以防读写冲突 切⽚是指针类型,数组是值类型 数组的长度是固定的,⽽切⽚不是(切⽚是动态的数组) 切⽚的底层是数组。切⽚可以通过数组来初始化,也可以通过内置函数make()初始化。初始化时len=cap,在追加元素时如果容量cap 不⾜时将进⾏扩容。如果原Slice容量⼩于1024, 则新Slice容量将扩⼤为原来的2倍; 如果原Slice容量⼤于等于1024, 则新Slice容量将扩⼤为原来的1.25倍。 len() 可以⽤来查看数组或slice的长度 cap()可以⽤来查看数组或slice的容量 在数组中由于长度固定不可变,因此len(arr)和cap(arr)的输出永远相同 在slice中,len(sli)表⽰可见元素有⼏个(也即直接打印元素看到的元素个数),⽽cap(sli)表⽰所有元素有⼏个 ⼆、map map底层使⽤哈希表来实现的,哈希过程产⽣冲突使⽤的冲突解决办法是链地址法。 1.map解决冲突的⽅法 使⽤链地址法:当多个键被哈希到了同⼀个bucket时,也就是产⽣了哈希冲突。由于每个bucket可以存放8个键值对, 所以同⼀个bucket 存放超过8个键值对时就会创建⼀个桶, ⽤链表的⽅式将bucket关联起来。 2.map的扩容 因为不能放任它⽆休⽌的冲突下去,⽆休⽌冲突的话会影响读写性能,于是引⼊了负载因⼦的概念,计算⽅式为:负载因⼦=键数量/桶数量,当负载因⼦达到指定的值就会进⾏扩容操作。 go语⾔中的哈希表触发扩容的条件有两个: 负载因⼦ > 6.5时, 也即平均每个bucket存储的键值对达到6.5个 overflow数量 > 2^15时, 也即overflow数量超过32768时 第⼀种情况负载因⼦过⼤,使⽤增量扩容。 当负载因⼦过⼤时, 就新建⼀个bucket, 新的bucket长度是原来的2倍, 然后旧bucket数据搬迁到新的bucket。 考虑到如果map存储了数以亿计的key-value, ⼀次性搬迁将会造成⽐较⼤的延时, Go采⽤逐步搬迁策略, 即每次访问map时都会触发⼀次搬迁, 每次搬迁2个键值对。 第⼆种overflow数量过多,使⽤等量扩容。 所谓等量扩容, 实际上并不是扩⼤容量, buckets数量不变, 重新做⼀遍类似增量扩容的搬迁动作, 把松散的键值对重新排列⼀次, 以使bucket的使⽤率更⾼, 进⽽保证更快的存取。 在极端场景下, ⽐如不断的增删, ⽽键值对正好集中在⼀⼩部分的bucket, 这样会造成overflow的bucket数量增多, 但负载因⼦⼜不⾼, 从⽽⽆法执⾏增量搬迁的情况。 3.map并发 go语⾔的map数据结构并不是并发安全的。想要并发安全的使⽤map结构。 通常由⼏种⽅式: 为map加读写锁