比较array和slice

Posted shouzhuo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了比较array和slice相关的知识,希望对你有一定的参考价值。

array和slice功能非常像, 容易搞混. 本文比较下array和slice的区别. 然后黑一下.

go版本: go version go1.13.3 linux/amd64

1 array

类型相同, 长度固定的数据集合. 长度无法修改
类型为: [len]T (len不能省略)
值是一段连续的内存区域和一些额外属性

属性示例:

  • beg_addr 起始地址. 赋值时固定
  • len 长度. 赋值时固定
  • cap 保存实际长度. 赋值时固定,始终和len相同

要点:

  • 编译时需要确定长度. 需要显式指定长度, 或者使用...由编译器自动推断
  • go中都是值传递(pass by value). 因此赋值会复制数组本身.
  • 引用需要借助pointer

2 slice

可以看作是内存的视图,展示内存的一段区域
类型为: []T
值是一个指向内存的数据结构, 类似指向数组的指针

属性示例:

  • array 底层数组
  • len 长度. 赋值(包括reslice)时设置
  • cap 从起始地址开始,最多可以展示的内存长度. = 底层数组结束地址-起始地址

要点:

  • 由于传递的是指针, 函数内可以修改外部slice指向的底层数组.  
  • 多个slice可以指向同一个底层数组.

3 陷阱

3.1 append slice必须赋值

append可能会导致cap不足,需要重新分配内存. 此时需要修改slice指向的内存地址,len,cap. 但是由于append没法修改原slice变量的属性, 这就出现了矛盾: 需要修改原输入参数,但有不能产生副作用.
golang的解决方法是强制规定append的返回值必须赋值给slice.算是解决了忘记赋值的问题, 但是太丑陋了. 给slice提供一个append方法不是更简单直接吗?

3.2 append重新分配内存导致的混乱

多个slice可以指向同一块内存数据. 对其中一个slice(假设为A)做append后, A可能不再和其他slice共享一块内存数据

这种可能性受如下因素影响:

  1. slice是否满了 (len>=cap) 与运行时状态有关. 可能引起难以复现的bug
  2. slice增长算法的具体实现 可能出现不同go版本可能表现不同的诡异问题

我觉得, 作为一个slice, 就应该保持内心的纯粹, 不能贪婪. slice应该只做视图, 不应该有改变底层大小的能力. slice应该添加只读和可写的功能. 去掉append方法. 假设按照这种方法, 看下会有哪些改进.

  1. 由于slice不会修改底层数组的长度, 因此不会出现slice共享失效的问题. slice变成了纯粹了视图层.
  2. 可以对数组的一段数据做操作. 比如排序,求和等等. 和现有功能一样
  3. 引入只读slice后, 可以更安全的共享片段数据.
  4. slice也没有必要由编译器做. 给array对象添加一个slice方法就可以了.

而变长数组就交给vector库就可以了. 何必通过编译器做这些库应该做的那? 哦, golang不支持泛型. 难道是由于不支持泛型, 才搞出这种设计的?

以上是关于比较array和slice的主要内容,如果未能解决你的问题,请参考以下文章

详解go语言的array和slice

GO语言切片Slice数组Array的比较;利用多维切片接受EXCEL内容的输出

js里相似的方法比较系列(二)slice,splice,split方法区别

FCC 中级算法题 比较两个数组

再次认识slice和splice,比较总结

slice的比较?