比较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共享一块内存数据
这种可能性受如下因素影响:
- slice是否满了 (len>=cap) 与运行时状态有关. 可能引起难以复现的bug
- slice增长算法的具体实现 可能出现不同go版本可能表现不同的诡异问题
我觉得, 作为一个slice, 就应该保持内心的纯粹, 不能贪婪. slice应该只做视图, 不应该有改变底层大小的能力. slice应该添加只读和可写的功能. 去掉append方法. 假设按照这种方法, 看下会有哪些改进.
- 由于slice不会修改底层数组的长度, 因此不会出现slice共享失效的问题. slice变成了纯粹了视图层.
- 可以对数组的一段数据做操作. 比如排序,求和等等. 和现有功能一样
- 引入只读slice后, 可以更安全的共享片段数据.
- slice也没有必要由编译器做. 给array对象添加一个slice方法就可以了.
而变长数组就交给vector库就可以了. 何必通过编译器做这些库应该做的那? 哦, golang不支持泛型. 难道是由于不支持泛型, 才搞出这种设计的?
以上是关于比较array和slice的主要内容,如果未能解决你的问题,请参考以下文章
GO语言切片Slice数组Array的比较;利用多维切片接受EXCEL内容的输出