教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 go语言坑之list删除所有元素

go语言坑之list删除所有元素

发布时间:2022-02-16   编辑:jiaochengji.com
教程集为您提供go语言坑之list删除所有元素等资源,欢迎您收藏本站,我们将为您提供最新的go语言坑之list删除所有元素资源

go提供了一个list包,类似python的list,可以存储任意类型的数据,并提供了相应的API,如下:

<pre><code>type <span class="hljs-keyword">Element</span> func (e *<span class="hljs-keyword">Element</span>) <span class="hljs-keyword">Next</span>() *<span class="hljs-keyword">Element</span> func (e *<span class="hljs-keyword">Element</span>) Prev() *<span class="hljs-keyword">Element</span> type <span class="hljs-keyword">List</span> func New() *<span class="hljs-keyword">List</span> func (l *<span class="hljs-keyword">List</span>) <span class="hljs-keyword">Back</span>() *<span class="hljs-keyword">Element</span> func (l *<span class="hljs-keyword">List</span>) <span class="hljs-keyword">Front</span>() *<span class="hljs-keyword">Element</span> func (l *<span class="hljs-keyword">List</span>) Init() *<span class="hljs-keyword">List</span> func (l *<span class="hljs-keyword">List</span>) InsertAfter(v interface<span class="hljs-list">{}</span>, mark *<span class="hljs-keyword">Element</span>) *<span class="hljs-keyword">Element</span> func (l *<span class="hljs-keyword">List</span>) InsertBefore(v interface<span class="hljs-list">{}</span>, mark *<span class="hljs-keyword">Element</span>) *<span class="hljs-keyword">Element</span> func (l *<span class="hljs-keyword">List</span>) Len() int func (l *<span class="hljs-keyword">List</span>) MoveAfter(e, mark *<span class="hljs-keyword">Element</span>) func (l *<span class="hljs-keyword">List</span>) MoveBefore(e, mark *<span class="hljs-keyword">Element</span>) func (l *<span class="hljs-keyword">List</span>) MoveToBack(e *<span class="hljs-keyword">Element</span>) func (l *<span class="hljs-keyword">List</span>) MoveToFront(e *<span class="hljs-keyword">Element</span>) func (l *<span class="hljs-keyword">List</span>) PushBack(v interface<span class="hljs-list">{}</span>) *<span class="hljs-keyword">Element</span> func (l *<span class="hljs-keyword">List</span>) PushBackList(other *<span class="hljs-keyword">List</span>) func (l *<span class="hljs-keyword">List</span>) PushFront(v interface<span class="hljs-list">{}</span>) *<span class="hljs-keyword">Element</span> func (l *<span class="hljs-keyword">List</span>) PushFrontList(other *<span class="hljs-keyword">List</span>) func (l *<span class="hljs-keyword">List</span>) <span class="hljs-keyword">Remove</span>(e *<span class="hljs-keyword">Element</span>) interface<span class="hljs-list">{}</span></code></pre>

借助list包提供的API,list用起来确实挺方便,但是在使用过程中,如果不注意就会遇到一些难以发现的坑,导致程序结果不是预想的那样。这里要说的坑是通过for循环遍历list,并删除所有元素时会遇到的问题。例如,下面这个示例程序创建了一个list,并依次将0-3存入,然后通过for循环遍历list删除所有元素:

<pre><code>package main import ( <span class="hljs-string">"container/list"</span> <span class="hljs-string">"fmt"</span> ) func main() { l := list<span class="hljs-preprocessor">.New</span>() l<span class="hljs-preprocessor">.PushBack</span>(<span class="hljs-number">0</span>) l<span class="hljs-preprocessor">.PushBack</span>(<span class="hljs-number">1</span>) l<span class="hljs-preprocessor">.PushBack</span>(<span class="hljs-number">2</span>) l<span class="hljs-preprocessor">.PushBack</span>(<span class="hljs-number">3</span>) fmt<span class="hljs-preprocessor">.Println</span>(<span class="hljs-string">"original list:"</span>) prtList(l) fmt<span class="hljs-preprocessor">.Println</span>(<span class="hljs-string">"deleted list:"</span>) for e := l<span class="hljs-preprocessor">.Front</span>()<span class="hljs-comment">; e != nil; e = e.Next() {</span> l<span class="hljs-preprocessor">.Remove</span>(e) } prtList(l) } func prtList(l *list<span class="hljs-preprocessor">.List</span>) { for e := l<span class="hljs-preprocessor">.Front</span>()<span class="hljs-comment">; e != nil; e = e.Next() {</span> fmt<span class="hljs-preprocessor">.Printf</span>(<span class="hljs-string">"%v "</span>, e<span class="hljs-preprocessor">.Value</span>) } fmt<span class="hljs-preprocessor">.Printf</span>(<span class="hljs-string">"\n"</span>) } </code></pre>

运行程序输出如下:

<pre><code>original <span class="hljs-keyword">list</span>: <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> deleted <span class="hljs-keyword">list</span>: <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> </code></pre>

从输出可以知道,list中的元素并没有被完全删除,仅删除了第一个元素0,和最初设想不一样,按照go的使用习惯,遍历一个list并删除所有元素写法应该如下:

<pre><code>for e := l.<span class="hljs-keyword">Front</span>(); e != nil; e = e.<span class="hljs-keyword">Next</span>() <span class="hljs-list">{ l.Remove(e) }</span></code></pre>

但是根据上面示例代码的输出,这样删除list所有元素是无效的,那么问题出在哪呢?由for循环的机制可以知道,既然删除了第一个元素,没有删除第二个元素,肯定是第二次循环的条件无效,才导致循环退出,即执行完下面语句后:

<pre><code>l.<span class="hljs-keyword">Remove</span>(e)</code></pre>

e应该为nil,所以循环退出。在for循环中的l.Remove(e)语句前添加打印语句验证,例如添加如下语句:

<pre><code>fmt.Println("<span class="hljs-operator"><span class="hljs-keyword">delete</span> a element <span class="hljs-keyword">from</span> list<span class="hljs-string">")</span></span></code></pre>

运行程序输出如下:

<pre><code>original list: <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> deleted list: <span class="hljs-built_in">delete</span> <span class="hljs-operator">a</span> <span class="hljs-keyword">element</span> <span class="hljs-built_in">from</span> list <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> </code></pre>

可以看到,确实只循环了一次,循环就结束了。即当执行完语句l.Remove(e)后,e等于e.Next(),因为e.Next()为nil,导致e为nil,循环退出。为什么e.Next()会是nil呢?通过查看go list源码,如下所示:

<pre><code>// remove removes e from its list, decrements l<span class="hljs-preprocessor">.len</span>, <span class="hljs-keyword">and</span> returns e. func (l *List) remove(e *Element) *Element { e<span class="hljs-preprocessor">.prev</span><span class="hljs-preprocessor">.next</span> = e<span class="hljs-preprocessor">.next</span> e<span class="hljs-preprocessor">.next</span><span class="hljs-preprocessor">.prev</span> = e<span class="hljs-preprocessor">.prev</span> e<span class="hljs-preprocessor">.next</span> = nil // avoid memory leaks e<span class="hljs-preprocessor">.prev</span> = nil // avoid memory leaks e<span class="hljs-preprocessor">.list</span> = nil l<span class="hljs-preprocessor">.len</span>-- return e } // Remove removes e from l if e is an element of list l. // It returns the element value e<span class="hljs-preprocessor">.Value</span>. func (l *List) Remove(e *Element) interface{} { if e<span class="hljs-preprocessor">.list</span> == l { // if e<span class="hljs-preprocessor">.list</span> == l, l must have been initialized when e was inserted // <span class="hljs-keyword">in</span> l <span class="hljs-keyword">or</span> l == nil (e is a zero Element) <span class="hljs-keyword">and</span> l<span class="hljs-preprocessor">.remove</span> will crash l<span class="hljs-preprocessor">.remove</span>(e) } return e<span class="hljs-preprocessor">.Value</span> }</code></pre>

由源码中可以看到,当执行l.Remove(e)时,会在内部调用l.remove(e)方法删除元素e,为了避免内存泄漏,会将e.next和e.prev赋值为nil,这就是问题根源。

修正程序如下:

<pre><code>package main import ( <span class="hljs-string">"container/list"</span> <span class="hljs-string">"fmt"</span> ) func main() { l := list<span class="hljs-preprocessor">.New</span>() l<span class="hljs-preprocessor">.PushBack</span>(<span class="hljs-number">0</span>) l<span class="hljs-preprocessor">.PushBack</span>(<span class="hljs-number">1</span>) l<span class="hljs-preprocessor">.PushBack</span>(<span class="hljs-number">2</span>) l<span class="hljs-preprocessor">.PushBack</span>(<span class="hljs-number">3</span>) fmt<span class="hljs-preprocessor">.Println</span>(<span class="hljs-string">"original list:"</span>) prtList(l) fmt<span class="hljs-preprocessor">.Println</span>(<span class="hljs-string">"deleted list:"</span>) var next *list<span class="hljs-preprocessor">.Element</span> for e := l<span class="hljs-preprocessor">.Front</span>()<span class="hljs-comment">; e != nil; e = next {</span> next = e<span class="hljs-preprocessor">.Next</span>() l<span class="hljs-preprocessor">.Remove</span>(e) } prtList(l) } func prtList(l *list<span class="hljs-preprocessor">.List</span>) { for e := l<span class="hljs-preprocessor">.Front</span>()<span class="hljs-comment">; e != nil; e = e.Next() {</span> fmt<span class="hljs-preprocessor">.Printf</span>(<span class="hljs-string">"%v "</span>, e<span class="hljs-preprocessor">.Value</span>) } fmt<span class="hljs-preprocessor">.Printf</span>(<span class="hljs-string">"\n"</span>) }</code></pre>

运行程序输出如下:

<pre><code>original <span class="hljs-keyword">list</span>: <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> deleted <span class="hljs-keyword">list</span>: </code></pre>

可以看见,list中的所有元素已经被正确删除。

<h5>本次荐书:简单的逻辑学</h5>

到此这篇关于“go语言坑之list删除所有元素”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
go语言坑之list删除所有元素
golang遍历时修改被遍历对象
Golang标准库——container/list
Go语言发展历史、核心、特性及学习路线
Python list列表删除元素的3种方法
Go语言List的使用与数据结构的选择
python列表常用方法快速攻略
用go语言实现查找两个数组的异同
REDIS基础, GO语言
python删除list中的重复元素

[关闭]
~ ~