go语言坑之list删除所有元素
时间:2022-05-14
本文章向大家介绍go语言坑之list删除所有元素,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
30 Mar 2017 go语言坑之list删除所有元素
go提供了一个list包,类似python的list,可以存储任意类型的数据,并提供了相应的API,如下:
type Element
func (e *Element) Next() *Element
func (e *Element) Prev() *Element
type List
func New() *List
func (l *List) Back() *Element
func (l *List) Front() *Element
func (l *List) Init() *List
func (l *List) InsertAfter(v interface{}, mark *Element) *Element
func (l *List) InsertBefore(v interface{}, mark *Element) *Element
func (l *List) Len() int
func (l *List) MoveAfter(e, mark *Element)
func (l *List) MoveBefore(e, mark *Element)
func (l *List) MoveToBack(e *Element)
func (l *List) MoveToFront(e *Element)
func (l *List) PushBack(v interface{}) *Element
func (l *List) PushBackList(other *List)
func (l *List) PushFront(v interface{}) *Element
func (l *List) PushFrontList(other *List)
func (l *List) Remove(e *Element) interface{}
借助list包提供的API,list用起来确实挺方便,但是在使用过程中,如果不注意就会遇到一些难以发现的坑,导致程序结果不是预想的那样。这里要说的坑是通过for循环遍历list,并删除所有元素时会遇到的问题。例如,下面这个示例程序创建了一个list,并依次将0-3存入,然后通过for循环遍历list删除所有元素:
package main
import (
"container/list"
"fmt"
)
func main() {
l := list.New()
l.PushBack(0)
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
fmt.Println("original list:")
prtList(l)
fmt.Println("deleted list:")
for e := l.Front(); e != nil; e = e.Next() {
l.Remove(e)
}
prtList(l)
}
func prtList(l *list.List) {
for e := l.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value)
}
fmt.Printf("n")
}
运行程序输出如下:
original list:
0 1 2 3
deleted list:
1 2 3
从输出可以知道,list中的元素并没有被完全删除,仅删除了第一个元素0,和最初设想不一样,按照go的使用习惯,遍历一个list并删除所有元素写法应该如下:
for e := l.Front(); e != nil; e = e.Next() {
l.Remove(e)
}
但是根据上面示例代码的输出,这样删除list所有元素是无效的,那么问题出在哪呢?由for循环的机制可以知道,既然删除了第一个元素,没有删除第二个元素,肯定是第二次循环的条件无效,才导致循环退出,即执行完下面语句后:
l.Remove(e)
e应该为nil,所以循环退出。在for循环中的l.Remove(e)语句前添加打印语句验证,例如添加如下语句:
fmt.Println("delete a element from list")
运行程序输出如下:
original list:
0 1 2 3
deleted list:
delete a element from list
1 2 3
可以看到,确实只循环了一次,循环就结束了。即当执行完语句l.Remove(e)后,e等于e.Next(),因为e.Next()为nil,导致e为nil,循环退出。为什么e.Next()会是nil呢?通过查看go list源码,如下所示:
// remove removes e from its list, decrements l.len, and returns e.
func (l *List) remove(e *Element) *Element {
e.prev.next = e.next
e.next.prev = e.prev
e.next = nil // avoid memory leaks
e.prev = nil // avoid memory leaks
e.list = nil
l.len--
return e
}
// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
func (l *List) Remove(e *Element) interface{} {
if e.list == l {
// if e.list == l, l must have been initialized when e was inserted
// in l or l == nil (e is a zero Element) and l.remove will crash
l.remove(e)
}
return e.Value
}
由源码中可以看到,当执行l.Remove(e)时,会在内部调用l.remove(e)方法删除元素e,为了避免内存泄漏,会将e.next和e.prev赋值为nil,这就是问题根源。
修正程序如下:
package main
import (
"container/list"
"fmt"
)
func main() {
l := list.New()
l.PushBack(0)
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
fmt.Println("original list:")
prtList(l)
fmt.Println("deleted list:")
var next *list.Element
for e := l.Front(); e != nil; e = next {
next = e.Next()
l.Remove(e)
}
prtList(l)
}
func prtList(l *list.List) {
for e := l.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value)
}
fmt.Printf("n")
}
运行程序输出如下:
original list:
0 1 2 3
deleted list:
可以看见,list中的所有元素已经被正确删除。
本次荐书:简单的逻辑学
LEo at 21:05
- HttpURLConnection实现两个服务端的对接
- java获取properties配置文件值
- 安全退出app,activoty栈管理
- JavaBean转Map方法
- JsBridge实现JavaScript和Java的互相调用
- JAVA-FTP批量大文件传输
- 独家 | 一文读懂TensorFlow(附代码、学习资料)
- 解决openssh漏洞,升级openssh版本
- 解决NTPD漏洞,升级Ntpd版本
- 独家 | 手把手教TensorFlow(附代码)
- HBase Region自动切分细节
- eclipse搭建ssh后台
- 解决mysql漏洞 Oracle MySQL Server远程安全漏洞(CVE-2015-0411)
- im4java包处理图片
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Pandas tricks 之 transform的用法
- Springboot + RabbitMQ 用了消息确认机制,感觉掉坑里了!
- 一款功能简约到可怜的SQL 客户端!
- 震惊!ConcurrentHashMap里面也有死循环,作者留的“彩蛋”?
- Python GUI项目实战(六)实现添加学生信息的功能
- 打卡群刷题总结0816——三角形最小路径和
- 打卡群刷题总结0814——二叉树展开为链表
- 打卡群刷题总结0813——二叉树展开为链表
- 打卡群刷题总结0812——路径总和 II
- SQL中CASE表达式的妙用
- 2w 字 + 40 张图带你参透并发编程!
- RSA 敏感数据加解密方案
- 极客算法训练笔记(一),算法学习方法篇
- 链表:听说用虚拟头节点会方便很多?
- 从JVM设计者的角度来看.class文件结构,一文弄懂.class文件的身份地位