我正在使用https://golang.org/pkg/container/list/的Golang中对我的LRU缓存解决方案进行微优化。我的解决方案通过使用map[int]*list.Element
来工作,其中每个list.List
list.Element
是[]int
,其中[0]
是键,而[1]
是值。 / p>
为了获得最佳效果,我尝试从[]int
移至[2]int
,但随后遇到的问题是,在ee := e.Value.([2]int)
之后修改固定大小的数组(请注意固定大小数组的[2]int
类型)不再修改list.Element
中的基础值,而w / ee := e.Value.([]int)
则不同(请注意[]int
类型) ,因为切片是基于引用的,而固定大小的数组是基于复制的值的,所以我认为这很合理。
我尝试了诸如e.Value.([2]int)[1] = …
之类的东西,以及与:= &e.Value…
的各种组合,并从[2]int
转换为[]int
,但是所有这些都会导致编译器错误。
Q: 是否无法将container/list
与嵌入式数组配合使用,并就地对所述固定大小的数组进行修改? < / p>
答案 0 :(得分:1)
您已经注意到:
我想这很合理,因为切片是基于引用的,而固定大小的数组是基于复制的值的
因此,如果要执行此操作,则需要通过将指针而不是数组值存储在数组中来使用对固定大小数组的引用。
这样,您将能够通过list元素修改基础数组。
请参见此处的简单示例:
package main
import (
"container/list"
"fmt"
)
func main() {
l := list.New()
// create a fixed size array and initialize it
var arr [2]int
arr[0] = 1
arr[1] = 2
// push a pointer to the array into the list
elem := l.PushFront(&arr)
// modify the stored array
elem.Value.(*[2]int)[0] = 3
// print the element from iterating the list
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
// print the underlying array, both are modified
fmt.Println(arr)
}
编辑
请注意,此行为不是此列表实现所特有的,而是与类型断言在语言本身中的工作方式有关。
查看此处:
https://golang.org/doc/effective_go.html#interface_conversions
从该部分引用(并增加我自己的强调):
语法从打开类型开关的子句中借用,但具有显式类型而不是type关键字:
value.(typeName)
,并且结果是具有静态类型typeName的新值。
使用引用类型时,复制值不会影响您,因为复制指针值最终会使您可以更改相同的基础引用。
但是当数组本身是一个值时,将其分配给副本就没有意义。当您尝试直接修改数组(不将类型断言分配给变量)时,Go甚至会在编译时捕获该数组,因此您不必分配给该“临时”副本,这显然是错误的。这就是您尝试执行此操作时所有语法错误的方式。
要克服这一点(如果您不想使用指针),一种可能是让您实现自己的list
,从您正在使用的实现中借用,但是将Element
设置一个显式[2]int
而不是一个接口。
这将消除对类型断言的需要,您将能够修改基础数组。