我是Go的新手,对此非常兴奋。但是,在我广泛使用的所有语言中:Delphi,C#,C ++,Python - 列表非常重要,因为它们可以动态调整大小,而不是数组。
在Golang中,确实有一个list.List
结构,但我看到的文档很少 - 无论是在Go By Example还是在我拥有的三本Go书中 - Summerfield,Chisnal和Balbaert--它们都是花费大量时间在数组和切片上,然后跳到映射。在源代码示例中,我也发现很少或根本没有使用list.List
。
似乎与Python不同,List不支持Range
- IMO的大缺点。我错过了什么吗?
切片当然很好,但它们仍然需要基于具有硬编码大小的数组。这就是List的用武之地。有没有办法在Go中创建一个没有硬编码数组大小的数组/切片?为什么要忽略List?
答案 0 :(得分:63)
几乎总是在考虑列表时 - 在Go中使用切片。切片是动态调整大小的。它们下面是一块连续的内存,可以改变大小。
它们非常灵活,您会看到是否阅读了SliceTricks wiki page。
以下是摘录: -
复制
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]
流行音乐
x, a = a[len(a)-1], a[:len(a)-1]
推
a = append(a, x)
更新:这是来自go团队本身的blog post all about slices的链接,它可以很好地解释切片与数组和切片内部之间的关系。
答案 1 :(得分:41)
因为我没有收到这个问题的明确答案(虽然我已经接受了一个答案)我现在要根据我所学的内容自己回答,因为我问过:
有没有办法在没有硬编码的Go中创建数组/切片 数组大小?
是。切片不需要来自slice
的硬编码数组:
var sl []int = make([]int,len,cap)
此代码分配大小为sl
且大小为len
- cap
且len
的大小为cap
的切片list.List
,这些变量可在运行时分配。
为什么忽略
list.List
?
似乎主要原因for i:=range sl {
sl[i]=i
}
在Go中似乎很少受到关注:
正如在@Nick Craig-Wood的回答中所解释的,有 几乎没有什么可以用无法完成的列表来完成 切片,通常更有效,更清洁,更多 优雅的语法。例如范围构造:
push_back
不能与list一起使用 - 需要C样式for循环。并在
很多情况下,C ++集合样式语法必须与列表一起使用:
list.List
等。
也许更重要的是,int
不是强类型的 - 它与Python的列表和词典非常相似,它允许在集合中混合各种类型。这似乎与此相反
对Go的处理方式。 Go是一种非常强类型的语言 - 例如,Go中从不允许隐式类型转换,即使从int64
到list.List
的upCast必须是
明确。但是list.List的所有方法都采用空接口 -
什么都可以。
我放弃Python并转移到Go的原因之一是因为
虽然是Python,但是在Python的类型系统中存在这种弱点
声称自己是强力打字的#34; (国际海事组织,它不是)。 Go似乎vector<T>
是一种&#34; mongrel&#34;,诞生于C ++的List()
和Python&#39; s
list.List
,在Go本身可能有点不合适。
如果在不太遥远的未来某个时刻,我们会发现list.List在Go中被弃用,虽然可能会保留,以适应那些罕见情况,即使使用良好的设计实践,最好用一个包含各种类型的集合来解决问题。或者也许是在那里提供一个桥梁&#34;为了让C家族的开发人员在了解切片的细微差别之前熟悉Go,这是Go,AFAIK独有的。 (在某些方面,切片看起来类似于C ++或Delphi中的流类,但并非完全相同。)
虽然来自Delphi / C ++ / Python背景,但在我初次接触Go时,我发现slice
比Go的切片更熟悉,因为我对Go感觉更舒服,我有回去把我的所有名单都改成了切片。我还没有找到map
和/或list.List
不允许我这样做的任何内容,因此我需要使用{{1}}。
答案 2 :(得分:9)
我认为这是因为对container/list
套餐的解释并不多,因为{em>> 一旦你吸收了什么是主要的Go用于处理通用数据的习惯用法。
在Delphi(没有泛型)或C中,你会在列表中存储指针或TObject
,然后在从列表中获取时将它们转换回真实类型。在C ++中,STL列表是模板,因此按类型进行参数化,在C#中(这些天)列表是通用的。
在Go中,container/list
存储类型interface{}
的值,这是一种特殊类型,能够表示任何其他(真实)类型的值 - 通过存储一对指针:一个类型信息包含的值,以及指向值的指针(如果值的大小不大于指针的大小,则直接指向值)。因此,当您想要向列表添加元素时,您只需将类型interface{}
的函数参数接受为任何类型的值。但是当你从列表中提取值,以及如何使用它们的真实类型时,你必须 type-asert 它们或者对它们执行类型转换 - 这两种方法都是只是以不同的方式做同样的事情。
以下是here:
的示例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()
获取元素的值,然后将其声明为int
原始插入值的类型。
您可以在&#34; Effective Go&#34;中阅读类型断言和类型开关。或任何其他介绍书。 container/list
软件包的文档总结了所有方法列表支持。
答案 3 :(得分:4)
请注意,Go切片可以通过append()
内置函数进行扩展。虽然这有时需要制作后备阵列的副本,但每次都不会发生这种情况,因为Go会超出新阵列的大小,使其容量大于报告的长度。这意味着可以在没有其他数据副本的情况下完成后续追加操作。
虽然您最终获得的数据副本多于使用链接列表实现的等效代码,但您无需单独分配列表中的元素以及更新Next
指针的需要。对于许多用途,基于数组的实现提供了更好或更好的性能,因此这是语言中强调的。有趣的是,Python的标准list
类型也支持数组,并且在追加值时具有类似的性能特征。
也就是说,有些情况下链接列表是更好的选择(例如,当您需要从长列表的开头/中间插入或删除元素时),这就是提供标准库实现的原因。我猜他们没有添加任何特殊的语言功能来处理它们,因为这些情况不像使用切片那样常见。
答案 4 :(得分:3)
除非切片过于频繁地更新(删除,在随机位置添加元素),切片的内存连续性将提供与链接列表相比的出色缓存命中率。
Scott Meyer谈论缓存的重要性.. https://www.youtube.com/watch?v=WDIkqP4JbkE
答案 5 :(得分:2)
来自:https://groups.google.com/forum/#!msg/golang-nuts/mPKCoYNwsoU/tLefhE7tQjMJ
It depends a lot on the number of elements in your lists, whether a true list or a slice will be more efficient when you need to do many deletions in the 'middle' of the list. #1 The more elements, the less attractive a slice becomes. #2 When the ordering of the elements isn't important, it is most efficient to use a slice and deleting an element by replacing it by the last element in the slice and reslicing the slice to shrink the len by 1 (as explained in the SliceTricks wiki)
<强>因此强>
使用切片
1.如果列表中的元素顺序不重要,您需要删除,只需要
使用List交换元素以删除最后一个元素,&amp;重新切片到(长度-1)
2.当元素更多时(无论更多意味着什么)
There are ways to mitigate the deletion problem --
e.g. 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.如果你需要遍历速度
答案 6 :(得分:2)
list.List
被实现为双向链表。如果不经常插入列表中间,基于数组的列表(C ++中的向量或golang中的切片)在大多数情况下比链接列表更好。尽管数组列表必须扩展容量并复制现有值,但追加的分摊时间复杂度对于数组列表和链表都是O(1)。由于数据结构中没有指针,数组列表具有更快的随机访问速度,更小的内存占用,更重要的是对垃圾收集器友好。