美文网首页
Golang切片slice存储微探索

Golang切片slice存储微探索

作者: 树袋Papa | 来源:发表于2019-05-23 17:41 被阅读0次

问题

不断将获取的信息存储进切片slice中,为了方便更新信息中的某些字段,建立了一个key和切片项地址的map,一边在切片中存储信息,一边建立Key-Value关系,导致通过key取到的值并不正确,也导致需要更新的字段没有更新,代码类似于:

var dataSlice []interface{}

var keyToData = make(map[string]interface{})

var id = 0

for info := fetchInfo(key) {

    dataSlice = append(dataSlice, info)

    // keyToData[key] = &info 

    keyToData[key] = &dataSlice[id]

    id++

}

info := keyToData[key] // 获取的info不是正确的data,更新的信息同步到dataSlice中

分析

map中保存的指针没有指向dataSlice中的项

测试


type test struct {
    ID string
}

func main() {
    strArr := []string{}
    testArr := []test{}
    intArr := []int{}

    strA := "info"
    strB := "mation"

    fmt.Printf("string: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &strA, &strB, strArr, &strArr)

    strArr = append(strArr, strA)
    fmt.Printf("string array: \t arr: %v, pointer: %p, A: %p\n", strArr, &strArr, &strArr[0])
    strArr = append(strArr, strB)
    fmt.Printf("string array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", strArr, &strArr, &strArr[0], &strArr[1])

    testA := test{ID: strA}
    testB := test{ID: strB}
    fmt.Printf("test: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &testA, &testB, testArr, &testArr)

    testArr = append(testArr, testA)
    fmt.Printf("test array: \t arr: %v, pointer: %p, A: %p\n", testArr, &testArr, &testArr[0])
    testArr = append(testArr, testB)
    fmt.Printf("test array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", testArr, &testArr, &testArr[0], &testArr[1])

    intA := 0
    intB := 1
    fmt.Printf("int: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &intA, &intB, intArr, &intArr)

    intArr = append(intArr, intA)
    fmt.Printf("int array: \t arr: %v, pointer: %p, A: %p\n", intArr, &intArr, &intArr[0])
    intArr = append(intArr, intB)
    fmt.Printf("int array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", intArr, &intArr, &intArr[0], &intArr[1])
}

输出结果

string:      A: 0xc42000e1f0, B: 0xc42000e200, Arr: [], ArrPtr: 0xc42000a060
string array:    arr: [info], pointer: 0xc42000a060, A: 0xc42000e210
string array:    arr: [info mation], pointer: 0xc42000a060, A: 0xc42000a100, B: 0xc42000a110



test:    A: 0xc42000e250, B: 0xc42000e260, Arr: [], ArrPtr: 0xc42000a080
test array:      arr: [{info}], pointer: 0xc42000a080, A: 0xc42000e270
test array:      arr: [{info} {mation}], pointer: 0xc42000a080, A: 0xc42000a180, B: 0xc42000a190



int:     A: 0xc420014068, B: 0xc420014080, Arr: [], ArrPtr: 0xc42000a0a0
int array:   arr: [0], pointer: 0xc42000a0a0, A: 0xc420014088
int array:   arr: [0 1], pointer: 0xc42000a0a0, A: 0xc4200140a0, B: 0xc4200140a8

讨论

  1. 需要注意的是,通过append在切片slice中存储时,进行的是值传递;
  2. 每次调用append时,都可能会造成整个切片存储内存上的重新调整,通过元素地址可以反映出,每次append时,golang都会尽力保证一段连续的内存进行元素存储;
  3. 所以前面map保存的元素地址,可能不会指向最终slice中的元素;
  4. 不管内存上如何调整,都保证了切片入口地址的不变,而且这个地址并不等同于第一个元素的地址,所以这个节点应该和C链表中头节点的功能类似;
  5. 那么如果通过make创建定长的切片,这时的切片性质上和数组等同,请看补充测试:

补充测试:

    test2Arr := make([]test, 2, 2)
    test3Arr := make([]interface{}, 2, 2)
    var test4Arr [2]interface{}
fmt.Printf("test 2: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &testA, &testB, test2Arr, &test2Arr)

    test2Arr = append(test2Arr, testA)
    fmt.Printf("test 2 array: \t arr: %v, pointer: %p, A: %p\n", test2Arr, &test2Arr, &test2Arr[0])
    test2Arr = append(test2Arr, testB)
    fmt.Printf("test 2 array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", test2Arr, &test2Arr, &test2Arr[0], &test2Arr[1])

    fmt.Printf("test 3: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &testA, &testB, test3Arr, &test3Arr)

    test3Arr[0] = &testA
    fmt.Printf("test 3 array: \t arr: %v, pointer: %p, A: %p\n", test3Arr, &test3Arr, test3Arr[0])
    test3Arr[1] = &testB
    fmt.Printf("test 3 array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", test3Arr, &test3Arr, test3Arr[0], test3Arr[1])

    fmt.Printf("test 4: \t A: %p, B: %p, Arr: %v, ArrPtr: %p\n", &testA, &testB, test4Arr, &test4Arr)

    test4Arr[0] = &testA
    fmt.Printf("test 3 array: \t arr: %v, pointer: %p, A: %p\n", test4Arr, &test4Arr, test4Arr[0])
    test4Arr[1] = &testB
    fmt.Printf("test 3 array: \t arr: %v, pointer: %p, A: %p, B: %p\n\n\n\n", test4Arr, &test4Arr, test4Arr[0], test4Arr[1])

结果输出

test 2:      A: 0xc420066220, B: 0xc420066230, Arr: [{} {}], ArrPtr: 0xc420086080
test 2 array:    arr: [{} {} {info}], pointer: 0xc420086080, A: 0xc420072080
test 2 array:    arr: [{} {} {info} {mation}], pointer: 0xc420086080, A: 0xc420072080, B: 0xc420072090



test 3:      A: 0xc420066220, B: 0xc420066230, Arr: [<nil> <nil>], ArrPtr: 0xc4200860c0
test 3 array:    arr: [0xc420066220 <nil>], pointer: 0xc4200860c0, A: 0xc420066220
test 3 array:    arr: [0xc420066220 0xc420066230], pointer: 0xc4200860c0, A: 0xc420066220, B: 0xc420066230



test 4:      A: 0xc420066220, B: 0xc420066230, Arr: [<nil> <nil>], ArrPtr: 0xc420086100
test 3 array:    arr: [0xc420066220 <nil>], pointer: 0xc420086100, A: 0xc420066220
test 3 array:    arr: [0xc420066220 0xc420066230], pointer: 0xc420086100, A: 0xc420066220, B: 0xc420066230

结论

  1. test 2 验证了如果make定长的切片,即使通过append添加存储,也不会引起元素内存的重新分配
  2. test 3 和test 4 验证了make定长的切片和数组等同;
  3. 虽然性质等同,但是append还是可以为slice添加元素,通过下标进行赋值时,不能进行容量扩展,报越界错误。

@author liu shuohui
@date 2019-05-23

相关文章

  • Golang切片slice存储微探索

    问题 不断将获取的信息存储进切片slice中,为了方便更新信息中的某些字段,建立了一个key和切片项地址的map,...

  • Golang 数组 切片

    Golang 数组和切片 go提供了相应数据类型来存储多个相同类型的元素。它们分别是数组和slice切片。其中,切...

  • Go-Slice

    golang 切片(Slice) 切片为引用类型,切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基...

  • Learn Golang in 21 Days - Day 10

    Learn Golang in 21 Days - Day 10 知识点 切片Slice Slice是对数组的抽象...

  • golang:slice切片

    golang中的切片的索引是左闭右开的,切片不存储数据,它是对数组的引用,其他切片可以跟它共享同一个底层的数组,所...

  • Golang——切片slice

    切片是一个拥有相同类型元素的可变长度的序列,底层基于数组类型进行封装。切片是一个引用类型,内部结构包括地址(切片中...

  • Golang 中的数组 (array) 和切片 (slice)

    huangwenwei - 字里行间 | Golang 中的数组 (array) 和切片 (slice) 中文描述...

  • Golang 入门 : 切片(slice)

    切片(slice)是 Golang 中一种比较特殊的数据结构,这种数据结构更便于使用和管理数据集合。切片是围绕动态...

  • Golang 入门 : 切片(slice)

    切片(slice)是 Golang 中一种比较特殊的数据结构,这种数据结构更便于使用和管理数据集合。切片是围绕动态...

  • Go 学习之路:引用类型与值类型

    Golang中只有三种引用类型:slice(切片)、map(字典)、channel(管道); 引用类型 引用类型理...

网友评论

      本文标题:Golang切片slice存储微探索

      本文链接:https://www.haomeiwen.com/subject/mobbzqtx.html