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

东方客主 等级 483 0 0

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中的所有元素已经被正确删除。

收藏
评论区

相关推荐

golang 中神奇的 slice
声明:本文仅限于简书发布,其他第三方网站均为盗版,原文地址: golang 中神奇的 slice(https://links.jianshu.com/go?tohttps%3A%2F%2Fliqiang.io%2Fpost%2Fimagesliceingolang) 在 golang 中,似乎人们都不太喜欢使用 Linked List,甚至于原
List集合
Java的List集合 一、ArrayList 1.插入 java / 在元素序列尾部插入 / public boolean add(E e) { // 1. 检测是否需要扩容 ensureCapacityInternal(size 1); // Increments modCount // 2. 将新元素插入序列尾
统计字符串中字符出现的次数(Python版)
字符串转list python s 'aabbccd' list1 list(s) 方法一: python list1 'a', 'a', 'b', 'c', 'c', 'c', 'c' dict_cnt {} for value in list1: dict_cntvalue dict_cnt.get(value,
Python中的基本list操作
List是python中的基本数据结构之一,和Java中的ArrayList有些类似,支持动态的元素的增加。list还支持不同类型的元素在一个列表中,List is an Object。 最基本的创建一个列表的方法 myList \'a','b','c'\ 在python中list也是对象,所以他也有方法和属性,在ptython解释器中 使用h
list集合的一些处理
做了一个消消乐类似的小游戏,其中需要打乱部分内容,用的list打乱的方式,特此笔记 Collections.sort(list); // 顺序排列 System.out.println(list); Collections.shuffle(list); // 混乱的意思 System.out.println(list);
go 语言资源整理
Awesome GitHub Topic for Go(https://links.jianshu.com/go?tohttps%3A%2F%2Fgithub.com%2Ftopics%2Fgolang) Awesome Go(https://links.jianshu.com/go?tohttps%3A%2F%2F
java中List数组遍历删除
List数组遍历删除 环境 jdk8 junit 单元测试 正解java// 正解1, jdk自带的addAll方法 @Test public void test18() { String strs {"12","34","56","78","90"}; List<String list Ar
[concurrent-map]-并发map在go中的使用
关于我 通过学习和分享的过程,将自己工作中的问题和技术总结输出,希望菜鸟和老鸟都能通过自己的文章收获新的知识,并付诸实施。 引言Go语言原生的map类型并不支持并发读写。在Go 1.9之前,go语言标准库中并没有实现并发map。在Go 1.9中,引入了sync.Map。 concurrentmap的优势concurrentm
go语言坑之list删除所有元素
go提供了一个list包 类似python的list,可以存储任意类型的数据,并提供了相应的API,如下:type Element func (e Element) Next() Element func (e Element) Prev() Elementtype List func New() List func (l List)
一篇文章彻底弄懂理解和高效运用切片(slice)
slice,中文多译为“切片”,是 Go 语言在数组之上提供的一个重要的抽象数据类型。在 Go 语言中,绝大多数需要使用数组的场合,切片都实现了完美替代。并且和数组相比,切片提供了更通用、功能更强大且便捷的数据序列访问接口。 1. 切片究竟是什么在对切片一探究竟之前,我们先来简略了解一下 Go 语言中的数组。Go 语言数组是一个固定长度的、容纳同构类型元素的
一篇文章彻底弄懂go语言方法的本质
Go 语言不支持经典的面向对象语法元素,比如:类、对象、继承等。但 Go 语言也有方法(method)。和函数相比,Go 语言中的方法在声明形式上仅仅多了一个参数,Go 称之为 receiver 参数。而 receiver 参数正是方法与类型之间的纽带。Go 方法的一般声明形式如下:gofunc (receiver T/T) MethodName(参数列表)
我丢,去面试初级Java开发岗位,被问到泛型?
1、泛型的基础概念 1.1 为什么需要泛型 c List list new ArrayList();//默认类型是Object list.add("A123"); list.add("B234"); list.add("C345"); System.out.println(list);
list assignment index out of range
分析:list assignment index out of range:列表超过限制一种情况是:list[index]index超出范围另一种情况是:list是一个空的,没有一个元素,进行list[0]就会出现错误!
盘点Python列表生成式的三种方法
一、前言列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。 二、案例分析 三种方法要生成list \[1, 2, 3, 4, 5, 6, 7, 8, 9, 10\]可以用list(range(1, 11))。print(list(range(1, 11))) 如果要生成\[1x1, 2x2
四大内置核心函数式编程&&Lambda表达式
1. 基于Function函数式使用Lambda表达式Function:接收一个参数并返回结果的函数;R apply(T t):将此参数应用到函数中 import java.util.Arrays;import java.util.Collections;import java.util.List;import java.util.function.Fun