不太明白 golang 官方设计 slice 这种可变长数据结构的初衷在哪?是速度更快么还是?
为什么不能跟其他语言一样设计的易用一点呢,有没有大佬出来解惑一下的
感觉这个数据结构用起来要非常的小心,如果按照其他语言的模式来思考,就会产生意想不到的结果
比如下面这 2 个案例
1
trait 2019-07-09 23:58:16 +08:00 via iPhone
就是设计的不合理
|
2
BOYPT 2019-07-10 00:08:17 +08:00
一些 trade off 吧,也没哪里不易用啊,理解这个模型即可,常见用法都是 s = append(s, item), 旧的 s 不再使用
|
3
Vegetable 2019-07-10 00:09:44 +08:00
善用指针和 copy。
|
4
reus 2019-07-10 00:10:02 +08:00
|
5
sunny352787 2019-07-10 00:24:26 +08:00 3
你把 golang 当 C 用就明白了
|
6
jinliming2 2019-07-10 00:33:26 +08:00 via iPhone 1
好像一个是数组,一个是切片,数组长度是不能变的,声明多少就是多少,切片是对一个数组中一部分的引用。
写 C++ 的时候就有过这样的情况,你需要一个数组的话,就要在代码里写死长度,或是通过 new 来分配一个固定长度的数组,总之数组一旦创建,长度就不可变,如果容量不够的话,就要重新分配一个数组,然后把旧数据复制过去,然后把旧数组释放掉(代码里写死的长度的数组就不能扩展了)。 Go 其实也是一样的,数组一旦创建长度就不能变,因为向系统申请的连续内存空间已经确定了,后面可能已经存了其他数据,不可以直接越界。 而 Go 为了方便,提供了一个切片(语法上,数组是中括号里要写固定长度,而切片就直接是一对中括号),切片实际上就是个指针,底层需要一个数组来作为存储空间,切片的起始位置一定是在数组头(也可以往后移动,只要不超出数组长度范围就行,但是往后移动了就移不回来了,相当于少了一个存储空间),切片的长度一定小于等于数组长度。以此来提供灵活的数组访问。 在 append 的时候就会出现这样的问题,如果容量不够了,就和 C++ 一样,需要重新创建一个数组,把数据复制过去,只不过这个过程被自动化了,新数组容量我印象中是以两倍的方式来增长的。 其实我觉得如果你知道它的原理,再稍微知道一些 C++ 的东西,就觉得其实也没啥不合理的,知道了原理之后就不会出错了,而且还觉得相比 C++ 还挺好用的。 |
7
cest 2019-07-10 00:44:10 +08:00
你的其他语言不会都是些类 script 语言吧?
|
8
lynskylate 2019-07-10 00:51:59 +08:00 via Android
@jinliming2 #5 对比也应该用 vector 来对比吧,看似提供了数组的语法糖却容易让人产生混乱
|
9
nethard 2019-07-10 01:31:05 +08:00 via iPhone
切片的设计初衷应该是提供一种轻量级的基础数据结构作为语言特性,Go 对于其他重型数据结构的支持则有所欠缺,当然只是做 CRUD 的话也用不到什么太重型的。
|
10
yegle 2019-07-10 02:23:01 +08:00
nullprogram.com/blog/2019/06/30/ 看这个可能方便理解
|
11
bobuick 2019-07-10 06:39:40 +08:00
哪个高级点的语言不需要一个可变长的数组? 用 cpp 你不用 vector 么,写死自己要
|
12
xiadong1994 2019-07-10 06:53:31 +08:00
@reus C++的 vector 在按值传递的时候底层数组也会复制一遍,跟 Go 这种虽然是按值传递但是却是一层 shallow copy 的行为还是不太一样吧
|
13
kidlj 2019-07-10 07:10:15 +08:00 via iPhone 1
Ken Thompson 设计的哟。
|
14
ericgui 2019-07-10 08:58:48 +08:00 via Android
其实可变长度的数组是 ok 的,但能不能叫做 DynamicArray 之类的名字,其他语言的程序员也能看得懂嘛。叫 slice 简直不能太傻了
|
15
myyou 2019-07-10 10:09:50 +08:00
@ericgui 实际叫切片并没有什么问题,slice 底层实现的确是取数组的一个切片。而按照 c 语言的的动态数组来看,一旦确定长度,并不能更改,所以叫 DynamicArray 并不准确。
|
16
cokyhe 2019-07-10 10:33:49 +08:00
官方为了切 jj 用的
|
17
ThanksSirAlex 2019-07-10 11:06:29 +08:00
因为数组长度被设计成了不可变的,所以需要一个更灵活的结构,就像 java C#里面的 string 和 stringbuilder
|
18
tiedan 2019-07-10 11:11:04 +08:00
Golang 还有 nil != nil 呢
|
19
skadi 2019-07-10 11:16:05 +08:00
std::vector
|
20
ensonmj 2019-07-10 12:25:30 +08:00
对比下 rust 的 slice,就会觉得 go 的还不错了。。。
|
21
swulling 2019-07-10 12:31:42 +08:00 via iPhone
值传递有自己的好处,
|
22
calease 2019-07-10 12:53:12 +08:00
跟 slice 有什么关系。也根本不需要关心 slice 的 internal implementation。go 是 pass by value,所以如果要修改数据必须要 pass in pointer。如果别的需要修改数据的 pass in pointer 只有 slice 不用 pass in pointer 就会很 confusing。
|
23
sosilver 2019-07-10 13:04:03 +08:00 via Android
看起来有点不伦不类,像 std::span 和 std::vector 的混合体
|
24
fcten 2019-07-10 13:13:54 +08:00
因为易用和性能是冲突的,那么作为设计者就必然要权衡。
如果一个设计要损失 10%的性能,但是开发效率提升 100%,要不要? 如果一个设计要损失 50%的性能,但是开发效率提升 100%,要不要? 关于用其它语言的思维方式无法理解的问题,那是因为 Go 最初就是设计给 C/C++ 程序员用的。熟悉 C/C++ 思维模式的程序员应该很容易理解这些。 |
25
ChristopherWu 2019-07-10 22:20:40 +08:00
@fcten 这结论太虚了。从这个这么广的结论,你怎么样也要说『比如 go 的 slice 就为了 xx 的性能而舍弃了 xx 的易用,这是没办法的』
|
27
buzailianxi 2019-07-11 11:44:04 +08:00
其内部对 data 的引用变化 扩容时机 是造成困扰的主要原因。
|
28
scipio 2019-07-12 13:57:03 +08:00
答主被高级语言惯久了~
|
29
webee 2019-07-15 14:58:26 +08:00
一个 slice 值是对底层数组的一个固定切片,长度是不可变的,每次改变长度都是生成新的切片。
%p 对于 slice 来说返回的是底层数组的地址,真实的 slice 值的地址需要取地址符&。 |