如何创建一个包装container/list.List的golang结构体?

4

我对我的结构体包含list.List与*list.List之间的意外差异感到困惑。为什么以下内容不起作用?

type listHolder struct {
    id     int
    mylist list.List
}

func newListHolder(id int, text string) listHolder {
    var newLH listHolder
    newLH.mylist = *list.New()
    newLH.id = id
    newLH.mylist.PushBack(text)
    return newLH
}

func (l *listHolder) pushBack(text string) {
    l.mylist.PushBack(text)
}

func (l *listHolder) printAll() {
    for temp := l.mylist.Front(); temp != nil; temp = temp.Next() {
        fmt.Println(temp.Value)
    }
}


func main() {
    a := newListHolder(1, "first") 
    a.pushBack("second")
    fmt.Printf("listHolder %d length %d Front()= %v, Back()=%v\n",
         a.id, a.mylist.Len(), a.mylist.Front().Value, a.mylist.Back().Value)
    a.printAll()
}

这将输出如下结果,显示长度符合预期,但Front()和Back()方法无法使用。
listHolder 1 length 2 Front()= `<nil>`, Back()=<nil>
<nil>

如果我将结构定义为:
// Same thing with a pointer
type listHolderPtr struct {
    id     int
    mylist *list.List
}

func newListHolderPtr(id int, text string) listHolderPtr {
    var newLH listHolderPtr
    newLH.mylist = list.New()
    newLH.id = id
    newLH.mylist.PushBack(text)
    return newLH
}

这个结构体可以正常工作,但是显然,任何listHolder的副本都共享对同一列表的引用,这不是我想要的。 我需要能够复制周围的对象并获得内部列表的新副本。这可能吗?

请参见https://play.golang.org/p/KCtTwuvaS1R,了解我正在尝试做的事情的简化示例。在实际用例中,我将在列表中的每个listHolder中向后推送并弹出前面的内容,在一个复杂的嵌套循环中进行。


你似乎明白指针最终会被共享;你只是在问如何复制 *list.List 吗? - JimB
是的,也许这才是问题的关键。我曾认为解引用指针会使其像一个对象一样运行;我仍然保持着我的C++思维方式,并没有完全理解go中的值语义和指针语义。毫无疑问,惯用的方法是使用切片。 :-) 但是,有没有一种优雅的方法来复制*list.List? - renaissanceGeek
1
切片也会有同样的问题,因为它们包含指向支撑数组的指针。你可以通过将一个列表的元素复制到另一个列表来复制列表,这是在文档中用于迭代列表的简单for循环。 - JimB
1个回答

1

我认为@JimB的建议是一个不错的解决方案,每当你向listHolder.mylist中推送一个值时,制作本地列表副本可能会解决你的问题(前提是我正确理解了潜在的问题)。我试图想出一个实现如下:

package main

import (
    "container/list"
    "fmt"
)

type listHolder struct {
    id     int
    mylist list.List
}

func newListHolder(id int) listHolder { // don't push back when constructing a new listHolder
    var newLH listHolder
    newLH.mylist = *list.New()
    newLH.id = id
    return newLH
}

func (l *listHolder) pushBack(text string) {
    // create a temporary list to copy all old and the new value to
    tmpList := list.New()

    // copy all existing values from l.mylist
    for e := l.mylist.Front(); e != nil; e = e.Next() {
        fmt.Printf("pushing back '%v' from old list\n", e.Value)
        tmpList.PushBack(e.Value)
    }

    // push back the new value
    tmpList.PushBack(text)

    // print the new tmpList for debugging purposes
    for ele := tmpList.Front(); ele != nil; ele = ele.Next() {
        fmt.Printf("creating new list element: %v\n", ele.Value)
    }

    // replace l.mylist with tmpList
    l.mylist = *tmpList
    // another version of this solution could be to return a new (i.e. copied) 
    // *listHolder with all the old values and the new 'text' value
}

func (l *listHolder) printAll() {
    for temp := l.mylist.Front(); temp != nil; temp = temp.Next() {
        fmt.Println(temp.Value)
    }
}

func main() {
    a := newListHolder(1)
    a.pushBack("first")  // push a value to a
    a.pushBack("second") // push another value to a
    fmt.Printf("listHolder %d length %d Front()=%v, Back()=%v\n",
        a.id, a.mylist.Len(), a.mylist.Front().Value, a.mylist.Back().Value)
    a.printAll()
}

这段代码的输出结果为:
creating new list element: first                         // nothing to copy, only creating a new list element
pushing back 'first' from old list                       // copy element ...
creating new list element: first                         // ... from old list
creating new list element: second                        // and push new element to 'tmpList'
listHolder 1 length 2 Front()=first, Back()=second       // print a summary
first                                                    // of the
second                                                   // new list

如果我有一些模拟数据,我就可以进行更多的测试/调试。至少这段代码不需要*list.List也能正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接