为什么在Go中很less使用列表?

我对Go很陌生,对此很兴奋。 但是,在所有我已经广泛使用的语言中:Delphi,C#,C ++,Python – 列表非常重要,因为它们可以dynamicresize,而不是数组。

在Golang中,确实有一个list.List结构体,但是我看不到有关它的文档 – 无论是在Go By Example还是三个Go书(Summerfield,Chisnal和Balbaert)中,他们都花费了大量的时间然后切片,然后跳到地图。 在souce代码的例子中,我也很less或没有使用list.List

它似乎也不像Python, Range不支持List – 大缺点IMO。 我错过了什么吗?

切片当然是很好的,但他们仍然需要基于硬编码大小的arrays。 这就是List进来的地方。有没有一种方法可以在Go中创build一个数组/片段而不需要硬编码的数组大小? 为什么列表被忽略?

当你想要一个列表的时候,总是使用一个切片而不是Go。 切片dynamic地重新resize。 在他们的底下是可以改变大小的连续的记忆片段。

如果您阅读SliceTricks wiki页面 ,您会发现它们非常灵活。

这是一个摘录:

复制

 b = make([]T, len(a)) copy(b, a) // or b = append([]T(nil), a...) 

 a = append(a[:i], a[j:]...) 

删除

 a = append(a[:i], a[i+1:]...) // or a = a[:i+copy(a[i:], a[i+1:])] 

删除而不保留​​顺序

 a[i], a = a[len(a)-1], a[:len(a)-1] 

stream行的

 x, a = a[len(a)-1], a[:len(a)-1] 

 a = append(a, x) 

更新 :这是一个链接到博客文章的所有关于从去团队本身,这很好地解释了片和数组和片内部之间的关系切片。

几个月前我问这个问题的时候,我刚开始调查Go的时候。 从那以后,我每天都在阅读关于Go的内容,并在Go中进行编码。

因为我没有得到这个问题的明确答案(尽pipe我已经接受了一个答案),我现在要根据我所学到的来自己回答,因为我问了这个问题:

有没有一种方法来创build一个数组/切片没有硬编码数组大小?

是。 切片不需要硬编码的数组来slice

 var sl []int = make([]int,len,cap) 

这段代码分配的slice sl ,其大小为len ,容量为caplencap是可以在运行时分配的variables。

为什么list.List被忽略?

看来主要原因list.List在Go中似乎没有得到重视的是:

  • 正如在@Nick Craig-Wood的回答中所解释的那样,使用片断无法完成的列表几乎没有任何事情可做,通常更有效率,更清晰,更优雅的语法。 例如范围构造:

     for i:=range sl { sl[i]=i } 

    不能与列表一起使用 – 一个C风格的循环是必需的。 在许多情况下,C ++集合样式语法必须与列表一起使用: push_back

  • 也许更重要的是, list.List不是强types的 – 它与Python的列表和字典非常相似,它允许在集合中混合各种types。 这似乎与Go的做法相反。 Go是一种非常强大的types化语言 – 例如,在Go中永远不允许隐式types转换,甚至从intint64的upCast都必须是显式的。 但是list.List的所有方法都是空的接口 – 什么都可以。

    我放弃Python并转到Go的原因之一是由于Pythontypes系统的这种弱点,虽然Python声称是“强types”(IMO不是)。 Go的list.List似乎是一种由C ++的vector<T>和Python的List()的“mongrel”,在Go本身中也许有点list.List

如果在不久的将来某个时候,我们会发现list.List在Go中被弃用,尽pipe也许它仍然存在,以适应罕见的情况,即使使用良好的devise实践,也能最好地解决问题与持有各种types的集合。 或者,也许是为了让C系列开发人员在学习Go,AFAIK独有的切片的细微差别之前为Go提供舒适的“桥梁”。 (在某些方面,切片看起来类似于C ++或Delphi中的stream类,但并不完全)。

尽pipe来自Delphi / C ++ / Python背景,但是在我最初接触Go的时候,我发现list.List比Go的切片更为熟悉,因为我对Go更加熟悉,所以我已经把所有的列表改为切片。 我还没有发现任何slice和/或map不允许我做,所以我需要使用list.List

我认为这是因为没有太多的关于他们的说法,因为一旦你吸收了通用数据处理的主要成语,那么container/list包就不言自明了。

在Delphi(没有generics)或C中,你可以将指针或TObject存储在列表中,然后在从列表中获取时将它们转换回真实types。 在C ++中,STL列表是模板,因此按types进行参数化,而在C#中(现在)列表是通用的。

在Go中, container/list存储了types为interface{}值,它是一种能够表示任何其他(真实)types的值的特殊types的值 – 通过存储一对指针:一个指向包含值的types信息,一个指针的值(或直接的值,如果它的大小不大于指针的大小)。 所以当你想添加一个元素到列表中时,你只需要将它作为interface{}函数参数来接受任何types的值。 但是当你从列表中提取值时,以及如何处理它们的实际types,你必须键入它们,或者在它们上面进行types切换 ,这两种方法基本上是完全相同的。

这里是一个从这里取得的例子:

 package main import ("fmt" ; "container/list") func main() { var x list.List x.PushBack(1) x.PushBack(2) x.PushBack(3) for e := x.Front(); e != nil; e=e.Next() { fmt.Println(e.Value.(int)) } } 

在这里,我们使用e.Value()获得一个元素的值,然后将其作为原始插入值的types的inttypes声明。

您可以在“Effective Go”或任何其他介绍手册中阅读types断言和types开关。 container/list包的文档汇总了所有的方法列表支持。

请注意,Go切片可以通过append()内build函数进行扩展。 虽然这有时需要复制后备数组,但不会每次都发生,因为Go会超出新数组的大小,使得数组容量大于报告长度。 这意味着后续的追加操作可以在没有其他数据拷贝的情况下完成。

虽然结束了更多的数据副本,而不是使用链接列表实现的等效代码,但您不必单独分配列表中的元素,而需要更新“ Next指针。 对于许多用途来说,基于数组的实现提供了更好或更好的性能,所以这是语言中强调的。 有趣的是,Python的标准listtypes也是数组支持的,并且在附加值时具有类似的性能特征。

也就是说,在某些情况下,链表是一个更好的select(例如,当你需要插入或从长列表的中间插入元素时),这就是为什么提供标准库实现的原因。 我想他们没有添加任何特殊的语言function来处理它们,因为这些情况不如那些使用切片的情况。

除非切片更新方式太频繁(删除,随机位置添加元素),切片的内存连续性将提供优良的caching命中率与链表相比。

Scott Meyer谈到caching的重要性.. https://www.youtube.com/watch?v=WDIkqP4JbkE

来自: https : //groups.google.com/forum/#!msg/golang-nuts/mPKCoYNwsoU/tLefhE7tQjMJ


这取决于列表中元素的数量,
 无论是一个真正的清单还是一个切片都会更有效率
 当你需要在列表的“中间”做很多删除。

 #1
越多的元素,切片越不具吸引力。 

 #2
当元素的sorting不重要时,
 使用切片和切片是最有效的
 通过将其replace为切片中的最后一个元素来删除元素
 将这个切片重新拼成一个len
  (如SliceTricks维基解释)

所以
使用切片
1.如果列表中元素的顺序不重要,则需要删除
使用List交换要删除的元素与最后一个元素,并重新切片(长度为1)
2.当元素更多(无论更多的手段)


 There are ways to mitigate the deletion problem -- eg the swap trick you mentioned or just marking the elements as logically deleted. But it's impossible to mitigate the problem of slowness of walking linked lists. 

所以
使用切片
1.如果你需要遍历速度