Go的List操作上的一個小“坑”


轉自http://sharecore.net/blog/2014/01/09/the-trap-in-golang-list/

 

一直想不清楚一個問題,簡單設計的東西到底是“坑多”還是“坑少”呢? 復雜的設計,考慮的太全面,使用起來更麻煩,使用者容易陷入亂,落入自身的陷阱;而簡單的設計呢,在許多方面上又顧及不周,如果使用者對其“設計”沒仔細研究,或者其實現本身又是一個黑盒子,也容易掉入到設計本身遺留下來的“陷阱”。下面是我剛開始使用Go寫代碼時碰到的一個小“坑”,這個“坑”的原因我歸結為后者。

這個“小坑”來自於go的container/list package的使用上。導致“坑”的代碼大概如下所示:

package main

import (
  "container/list"
  "fmt"
)

func main() {
  //初始化一個list
  l := list.New()
  l.PushBack(1)
  l.PushBack(2)
  l.PushBack(3)
  l.PushBack(4)

  fmt.Println("Before Removing...")
  //遍歷list,刪除元素
  for e := l.Front(); e != nil; e = e.Next() {
      fmt.Println("removing", e.Value)
      l.Remove(e)
  }
  fmt.Println("After Removing...")
    //遍歷刪除完元素后的list
  for e := l.Front(); e != nil; e = e.Next() {
      fmt.Println(e.Value)
  }
}

以上代碼很簡單,按常理來看,應該能得到正確的結果,list最后將會被清空。可事實卻完全不是這樣,執行后結果如下:

Before Removing...
removing 1
After Removing...
2
3
4

從結果可以看出,list根本沒有清空,而只是刪除了第一個元素。這是為何?原因就在container/list package的實現上了。這應該是我見過的實現最簡單list了,出去注釋也就100來行實現代碼,而且它不只是一個簡單鏈表,而且可以當做stack,當做queue來使用。

下面是Remove方法的代碼:

// 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
}

這下問題原因就明顯了,就出現在e.next = nil 這行代碼上。當執行玩remove,e.next就變成了nil,list遍歷當然也就終止了。找出問題的原因,我們就容易找到workaround的辦法了,將e.next用中間變量保存起來就OK了,代碼如下:

package main

import (
    "container/list"
    "fmt"
)

func main() {
    l := list.New()
    l.PushBack(1)
    l.PushBack("asd")
    l.PushBack(3)
    l.PushBack(4)
    fmt.Println("Before Removing...")
    var n *list.Element
    for e := l.Front(); e != nil; e = n {
        fmt.Println("removing", e.Value)
        n = e.Next()
        l.Remove(e)
    }
    fmt.Println("After Removing...")
    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value)
    }
}

正常結果輸出:

Before Removing...
removing 1
removing 2
removing 3
removing 4
After Removing...

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM