• GO——数据容器


    数据容器

    数组与切片

    数组和切片有何异同

    切片本质上是对数组的封装,他描述一个数组的片段。

    数组是一片连续的地址空间,切片是一个结构体,包含长度、容量、底层数组。

    // src/runtime/slice.go
    
    type slice struct {
        array unsafe.Pointer //元素指针
        len  int  //  长度
         cap  int // 容量
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意:底层数组可以被多个切片同时指向

    切片如何被截取

    上文说过,底层数组是可以被共用的,

    那么 切片被截取的完成后,需要考虑 ——新旧slice 的底层数组是不是共用的。

    一般情况下,只有slice 继续append 扩充长度,才会导致底层数组不可用

    func main() {
    	slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    
    	s1 := slice[2:5]
    	s2 := slice[2:6:7]
    
    	s2 = append(s2, 100)
    	s2 = append(s2, 200)
    
    	s1[2] = 20
    
    	fmt.Println(s1)
    	fmt.Println(s2)
    	fmt.Println(slice)
    }
    
    
    [2 3 20]
    [2 3 4 5 100 200]       
    [0 1 2 3 20 5 100 7 8 9]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    从上面的例子可以看出,s1 是与slice 公用底层数组,而s2 没有共用,是因为底层数组长度不够,进行了扩充。

    切片的容量是怎样增长的

    切片容量增长调用的 append 函数

    备注: 这里详细去看slice 的文档

    切片作为函数参数会被改变吗

    前文说到,slice 是作为一个结构体存在的,但是他的数组是以指针的形式。

    若直接传slice ,在调用者看来,实参slice 并不会被函数对形参的操作改变,实参是形参的一个复制;

    若传的是slice指针,则会影响实参。

    但是因为结构体里面是数组以指针的形式存在。

    那么在上面不论是直接传slice 还是 slice 指针,都是共用的一个底层数组。!!!

    另外提一句: go语言中的函数传参,只有值传递,没有引用传递。

    内建函数 make 和new 的区别是什么

    首先make 和new 都是用来分配内存的函数。但是适用的类型不同:

    make 适用于 slice ,map,channel 等引用函数。

    new 适用于 int 型、数组、结构体等值类型。

    散列表map

    map 的底层实现原理是什么

    Go 语言采用的是哈希查找表,并且使用链表法解决哈希冲突

    1. map 内存模型
    // src/runtime/map.go
    
    type hmap struct {
        //元素个数 ,调用 len(map) 直接返回值
        count int
        flags uint8
        //buckets 的对数 log_2
        B  uint8
        // overflow 的bucket 近似数
        noverflow uint16
        // 计算key的哈希的时候会传入哈希函数
        hash0 unt32
        // 指向buckets 数组 ,大小为 2^B
        // 如果元素个数为0,就为nil
        buckets unsafe.Pointer
        // 扩容的时候,buckets 长度会是oldbuckets 的两倍
        oldbuckets unsafe.Pointer
        // 指针扩容进度,小于此地址的buckets 完成迁移
        nevacuate uintptr
        extra *mapextra
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    map 中的key 为什么是无序的

    随着map 中的key 数量的增多,原有的空间无法保证高效的进行增删改查的操作时就会发生扩容。而map 扩容,会发生key 的搬迁。原来在同一个bucket 中的key ,搬迁后,有些key会发生变动,bucket 序号加上2^B

    上面描述了,假定,每次遍历bucket 是依次遍历的。map 是在有增删 的环境下,遍历是无序。

    那么为什么真实情况,map 还是无序的?

    在Go 的视线中,当遍历map 时,并不是固定从bucket 从0 开始遍历。而是每次都随机一个bucket 开始。

    所以 map 是无序的。

    float 类型可以作为map 的key 吗

    从语法来看,是可以的,Go 语言 只要是可比较类型都可以作为key ,除了 slice ,map,functions 这几种类型,其他类型都可以作为map 的key。 这些类型的共同特征是支持 == 和 != 操作符。如果是结构体,只有hash 后的值和字面值相当,才认同为相同的可以。

    如何比较两个map 是否相等

    在两个map 深度相等的条件如下:

    1. 都为nil
    2. 非空、长度相等,指向同一个map 实体对象
    3. 响应的key 指向value “深度相等”

    三个条件只要满足一条都认为是相等。(直接用== 是错误,相当于 判nil)

  • 相关阅读:
    模型资源加载引起的内存对齐问题
    【云原生之Docker实战】使用Docker部署Komga个人漫画服务器
    idea中java类属性(字段)链式赋值
    Spring04
    C++ 数字
    使用transformers过程中出现的bug
    C++中打印uint64_t
    关于vscode的GitLens插件里的FILE HISTORY理解
    三入职场!你可以从我身上学到这些(附毕业Vlog)
    密码暴力破解漏洞(kali crunch)
  • 原文地址:https://blog.csdn.net/xyjworkgame/article/details/126924900