我是Go的新手,对此非常兴奋.但是,在我广泛使用的所有语言中:Delphi,C#,C++,Python - 列表非常重要,因为它们可以动态调整大小,而不是数组.
在Golang中,确实存在一个list.List结构体,但我看到很少有关于它的文档 - 无论是Go Go Example还是我所拥有的三本Go书 - Summerfield,Chisnal和Balbaert--它们都花费大量时间在数组和切片上然后跳到地图.在源代码示例中,我也发现很少或没有使用list.List.
与Python不同,它似乎Range也不支持List - 大缺点IMO.我错过了什么吗?
切片当然很好,但它们仍然需要基于硬编码大小的数组.这就是List的用武之地.有没有办法在没有硬编码数组大小的Go中创建数组/切片?为什么List被忽略了?
Nic*_*ood 73
几乎总是当你想到一个列表时 - 在Go中使用切片.切片是动态调整大小的.它们下面是一块连续的内存,可以改变大小.
它们非常灵活,您将看到是否阅读了SliceTricks维基页面.
这是摘录: -
复制
Run Code Online (Sandbox Code Playgroud)b = make([]T, len(a)) copy(b, a) // or b = append([]T(nil), a...)切
Run Code Online (Sandbox Code Playgroud)a = append(a[:i], a[j:]...)删除
Run Code Online (Sandbox Code Playgroud)a = append(a[:i], a[i+1:]...) // or a = a[:i+copy(a[i:], a[i+1:])]删除而不保留订单
Run Code Online (Sandbox Code Playgroud)a[i], a = a[len(a)-1], a[:len(a)-1]流行的
Run Code Online (Sandbox Code Playgroud)x, a = a[len(a)-1], a[:len(a)-1]推
Run Code Online (Sandbox Code Playgroud)a = append(a, x)
更新:这是一个博客文章的链接,所有关于来自go团队本身的切片,这很好地解释了切片和数组之间的关系以及切片内部.
Vec*_*tor 44
几个月前,当我第一次开始调查Go时,我问过这个问题.从那时起,我每天都在阅读Go,并在Go中编码.
因为我没有得到这个问题的明确答案(虽然我已经接受了一个答案)我现在要根据我所学到的内容自己回答,因为我问过它:
有没有办法在没有硬编码数组大小的Go中创建数组/切片?
是.切片不需要硬编码数组slice来自:
var sl []int = make([]int,len,cap)
Run Code Online (Sandbox Code Playgroud)
此代码分配片sl,尺寸len,容量cap- len和cap是可在运行时被分配的变量.
为什么被
list.List忽略?
似乎主要原因list.List似乎在Go中很少受到关注:
正如在@Nick Craig-Wood的回答中所解释的那样,对于无法通过切片完成的列表,通常更高效,并且使用更清晰,更优雅的语法,几乎无法做任何事情.例如范围构造:
for i:=range sl {
sl[i]=i
}
Run Code Online (Sandbox Code Playgroud)
不能与列表一起使用 - 需要C样式for循环.在许多情况下,C++集合样式语法必须与列表一起使用:
push_back等等.
也许更重要的是,list.List它不是强类型的 - 它与Python的列表和词典非常相似,它允许在集合中混合各种类型.这似乎与Go的做法相反.Go是一种非常强类型的语言 - 例如,Go中从不允许隐式类型转换,即使upCast int也int64必须是显式的.但是list.List的所有方法都采用空接口 - 任何事情都可以.
我抛弃Python并转向Go的原因之一是因为Python的类型系统存在这种弱点,尽管Python声称"强类型"(IMO不是).Go list.List似乎是一种"杂种",出自C++ vector<T>和Python
List(),并且在Go本身可能有点不合适.
如果在不久的将来某个时刻,我们会发现list.List在Go中被弃用,虽然它可能会保留,以适应那些罕见的情况,即使使用良好的设计实践,也可以最好地解决问题,这不会让我感到惊讶.拥有各种类型的集合.或许它可以为C家族开发人员提供一个"桥梁",让他们在了解切片的细微差别之前熟悉Go,这是Go,AFAIK独有的.(在某些方面,切片看起来类似于C++或Delphi中的流类,但并非完全相同.)
虽然来自Delphi/C++/Python背景,但在我初次接触Go时,我发现list.List比Go的片段更熟悉,因为我对Go更加熟悉,我已经回去并将所有列表更改为切片.我还没有发现任何东西slice和/或map不允许我这样做,所以我需要使用list.List.
我认为这是因为没有太多关于它们的说法,因为一旦你吸收了使用泛型数据的主要Go成语,那么这个container/list包就是不言自明了.
在Delphi(没有泛型)或C中,您可以TObject在列表中存储指针或s,然后在从列表中获取时将它们转换回真实类型.在C++中,STL列表是模板,因此按类型进行参数化,在C#中(这些天)列表是通用的.
在Go中,container/list存储类型的值,interface{}这是一种特殊类型,能够表示任何其他(真实)类型的值 - 通过存储一对指针:一个指向所包含值的类型信息,以及指向该值的指针(或值直接,如果它的大小不大于指针的大小).因此,当您想要向列表中添加元素时,您只需将其作为类型为interface{}accept的值的函数参数.但是当你从列表中提取值,以及如何使用它们的真实类型时,你必须键入它们或者对它们进行类型切换 - 这两种方法只是做同样事情的不同方法.
这是一个从这里得到的例子:
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))
}
}
Run Code Online (Sandbox Code Playgroud)
这里我们获取元素的值e.Value(),然后将其断言为int原始插入值的类型.
您可以在"Effective Go"或任何其他入门书籍中阅读类型断言和类型开关.该container/list包的文档总结的所有方法列表的支持.
请注意,可以通过append()内置函数扩展 Go 切片。虽然这有时需要复制后备数组,但不会每次都发生,因为 Go 会使新数组过大,使其容量大于报告的长度。这意味着可以在没有另一个数据副本的情况下完成后续的追加操作。
虽然与使用链表实现的等效代码相比,您最终会得到更多的数据副本,但您无需单独分配列表中的元素,也无需更新Next指针。对于许多用途,基于数组的实现提供了更好或足够好的性能,因此这就是语言中所强调的。有趣的是,Python 的标准list类型也是数组支持的,并且在附加值时具有类似的性能特征。
也就是说,在某些情况下,链接列表是更好的选择(例如,当您需要从长列表的开头/中间插入或删除元素时),这就是提供标准库实现的原因。我猜他们没有添加任何特殊的语言功能来使用它们,因为这些情况比使用切片的情况少见。
来自: https: //groups.google.com/forum/#! msg/golang-nuts/mPKCoYNwsoU/tLefhE7tQjMJ
这在很大程度上取决于列表中元素的数量, 真正的列表还是切片会更有效 当您需要在列表的“中间”进行多次删除时。 #1 元素越多,切片的吸引力就越小。 #2 当元素的顺序不重要时, 使用切片是最有效的 通过将元素替换为切片中的最后一个元素来删除元素,并且 重新切片切片以将 len 缩小 1 (如 SliceTricks wiki 中所解释)
所以
使用切片
1. 如果列表中元素的顺序不重要,并且需要删除,只需
使用 List 将要删除的元素与最后一个元素交换,并重新切片为 (length-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.
Run Code Online (Sandbox Code Playgroud)
所以
使用切片
1。如果你需要遍历速度
除非切片更新太频繁(删除,在随机位置添加元素),与链表相比,切片的内存连续性将提供出色的缓存命中率。
Scott Meyer 关于缓存重要性的演讲.. https://www.youtube.com/watch?v=WDIkqP4JbkE