[面试题~]Golang

这篇具有很好参考价值的文章主要介绍了[面试题~]Golang。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

3. 数组和切片

3.1 数组和切片的区别

Go语言中数组是固定长度的,不能动态扩容,在编译期就会确定大小。
切片是一种数据结构,包含一个底层数组的指针,当前切片个数 len 以及切片的最大容量 cap, 描述的是一块数组。

3.2 切片的扩容策略

切片的扩容都是调用growslice方法,不同版本,扩容机制也有细微差距。
Go1.17 版本,切片在扩容时会进行内存对齐,这个和内存分配策略相关。进行内存对齐之后,新 slice 的容量是要 大于等于老 slice 容量的 2倍或者1.25倍。
当新切片需要的容量大于两倍扩容的容量,则直接按照新切片需要的容量扩容,当原 slice 容量小于 1024 的时候,新 slice 容量变成原来的 2 倍;原 slice 容量超过 1024,新 slice 容量变成原来的1.25倍。
在1.18时,又改成不和1024比较了,而是和256比较。
简单地说,就是小切片按照2倍扩容,大切片按照1.25倍扩容。
Go官方注释说这么做的目的是能更平滑的过渡。

3.3 Go 中对 nil 的 Slice 和空 Slice 的处理是一致的吗?

首先 Go 的 JSON 标准库对 nil slice 和 空 slice 的处理是不一致。

  • slice := make([]int,0):slice 不为 nil,但是 slice 没有值,slice 的底层的空间是空的。
  • slice := []int{} :slice 的值是 nil,可用于需要返回 slice 的函数,当函数出现异常的时候,保证函数依然会有 nil 的返回值。

4. map

4.1 map 的底层实现

Golang 中 map 的底层实现是一个散列表,因此实现 map 的过程实际上就是实现散表的过程。在这个散列表中,主要出现的结构体有两个,一个叫 hmap(a header for a go map),一个叫 bmap(a bucket for a Go map,通常叫其 bucket)。

4.2 map 如何扩容

扩容其实就是一个预分配内容的过程,在 map 中的表现是 预分配 bucket。

  1. 双倍扩容:扩容采取了一种称为“渐进式”的方式,原有的 key 并不会一 次性搬迁完毕,每次最多只会搬迁 2 个 bucket。
  2. 等量扩容:重新排列,极端情况下,重新排列也解决不了,map 存储就会蜕变成链表,性能大大降低,此时哈希因子 hash0 的设置,可以降低此类极端场景的发生。

4.3 什么时候会发生扩容

触发 map 扩容的时机:在向 map 插入新 key 的时候,会进行条件检测,符合下面这 2 个条件,就会触发扩容:

  1. 装载因子超过阈值
  2. overflow 的 bucket 数量过多

  • 对于条件 1,元素太多,而 bucket 数量太少,很简单:将 B 加 1,bucket 最大数量(2^B)直接变成原来 bucket 数量的 2 倍。于是,就有新老 bucket 了。注意,这时候元素都在老 bucket 里,还没迁移到新的 bucket 来。新 bucket 只是最大数量变为原来最大数量的 2 倍(2^B * 2) 。
  • 对于条件 2,其实元素没那么多,但是 overflow bucket 数特别多,说明很多 bucket 都没装满。解决办法就是开辟一个新 bucket 空间,将老 bucket 中的元素移动到新 bucket,使得同一个 bucket 中的 key 排列地更紧密。这样,原来,在 overflow bucket 中的 key 可以移动到 bucket 中来。结果是节省空间,提高 bucket 利用率,map 的查找和插入效率自然就会提升。

4.4 map 什么时候会发生迁移

map 扩容完毕后,不会马上就进行迁移。而是采取增量扩容的方式,当有访问到具体 bukcet 时,才会逐渐的进行迁移(将 oldbucket 迁移到 bucket)。

4.5 map 查找

Go 语言中 map 采用的是哈希查找表,由一个 key 通过哈希函数得到哈希值,64 位系统中就生成一个 64bit 的哈希值,由这个哈希值将 key 对应存到不同的桶 (bucket)中,当有多个哈希映射到相同的的桶中时,使用链表解决哈希冲突。
细节:key 经过 hash 后共 64 位,根据 hmap 中 B 的值,计算它到底要落在哪个桶时,桶的数量为 2B ,如 B=5,那么用 64 位最后 5 位表示第几号桶,在用hash 值的高 8 位确定在 bucket 中的存储位置,当前 bmap 中的 bucket 未找到,则查询对应的 overflow bucket,对应位置有数据则对比完整的哈希值, 确定是否是要查找的数据。如果当前 map 处于数据搬移状态,则优先从 oldbuckets 查找。

4.6 map 删除

删除的逻辑相对比较简单,核心是找到 key 的具体位置。在 bucket 中挨个 cell 寻找。找到对应位置后,对 key 或者 value 进行“清零”操作,将 count 值减 1,将对应位置的 tophash 值置成 Empty

4.7 为什么遍历map是无序的?

遍历的过程,就是按顺序遍历 bucket,同时按顺序遍历 bucket 中的 key,而每个 key 进入 bucket 之前都先进行了哈希散列,所以没办法保证遍历的有序性。

4.8 如何实现有序遍历map?

可以把 map 的 key 全部拿出来进行排序,然后根据 key 去获取 value 。

4.9 为什么Go map是非线程安全的?

因为hash map 的内存是按照2的倍数开辟的,当前面开辟的内存不够的时候,会新开辟一段内存,将原来内存的数据转移到新的内存块中,这个过程是没有加锁的,如果这个时候同时有个读的线程过来获取这块内存数据,就会出现安全问题。

4.10 线程安全的map如何实现?

避免map并发读写panic的方式之一就是加锁,考虑到读写性能,可以使用读写锁提供性能。

4.11 Go sync.map 和原生 map 谁的性能好,为什么?

在基准测试中,在并发安全的情况下sync.Map会比我们常用的map+读写锁更加的快,快了五倍,这是得以于只读read设计,减低锁的粒度。但是利用读写锁的话,我们存储的不是一个简单数据类型,而是一个指针对象,那么用普通map+读写锁能很好地控制锁的粒度,达到更好的操作。

4.12 为什么 Go map 的负载因子是 6.5?

源码里定义的阈值的 6.5 (loadFactorNum/loadFactorDen),是经过测试后取出的一个比较合理的因子
因为每个 bucket 有 8 个空位,在没有溢出,且所有的桶都装满了的情况下,装载因子算出来的结果是 8。因此当装载因子超过 6.5 时,表明很多 bucket 都快要装满了,查找效率和插入效率都变低了。在这个时候进行扩容是有必要的。文章来源地址https://www.toymoban.com/news/detail-800440.html

到了这里,关于[面试题~]Golang的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [面试题~]Golang

    3.1 数组和切片的区别 Go语言中数组是固定长度的,不能动态扩容,在编译期就会确定大小。 切片是一种数据结构,包含一个底层数组的指针,当前切片个数 len 以及切片的最大容量 cap, 描述的是一块数组。 3.2 切片的扩容策略 切片的扩容都是调用growslice方法,不同版本,扩

    2024年01月18日
    浏览(24)
  • Golang 面试题

    golang 面试题 - 诺唯的文章 - 知乎 https://zhuanlan.zhihu.com/p/26972862 Panic and defer panic 触发会停止函数运行,然后按照栈的顺序执行 defer,最后退出函数,终止整个程序 2. map map[li:0xc000010030 wang:0xc000010030 zhou:0xc000010030] Hello, 世界 stu 仅仅是一个用来拷贝的 struct, 取地址不变,可改为索

    2024年02月12日
    浏览(31)
  • [面试题~] Golang

    1.1 逃逸分析是什么? 在编译原理中,分析指针动态范围的方法称之为逃逸分析。在Go中的表现是,如果一个对象的指针被多个方法或线程引用时,则称这个指针发生了逃逸。 所以,我认为逃逸分析指的是,通过分析变量的指针作用范围,来决定这个变量是分配在堆上还是栈

    2024年01月18日
    浏览(23)
  • Golang企业面试题

     Golang企业面试题 基础 高级 Golang有哪些优势? Golang数据类型有哪些 Golang中的包如何使用 Go 支持什么形式的类型转换? 什么是 Goroutine?你如何停止它? 如何在运行时检查变量类型? Go 两个接口之间可以存在什么关系? Go 当中同步锁有什么特点?作用是什么 Go 语言当中

    2024年01月24日
    浏览(27)
  • golang面试题大全

    1、与其他语言相比,使用 Go 有什么好处? 与其他作为学术实验开始的语言不同, Go 代码的设计是务实的。每个功能和语法决策都旨在让程序员的生活更轻松。 Golang 针对并发进行了优化,并且在规模上运行良好。 由于单一的标准代码格式, Golang 通常被认为比其他语言更具

    2024年01月20日
    浏览(23)
  • golang—面试题大全

    =是赋值,:=是声明并初始化一个新的变量 在 Go 语言中,并没有像一些其他编程语言那样使用传统的异常处理机制,Go 语言使用了一种不同的错误处理模式,通过返回错误值来进行错误处理。Go 的错误处理模式更加简洁和明确,使用了多返回值来传递错误信息。在 Go 中,通常

    2024年02月13日
    浏览(30)
  • Golang中级面试题

    Goroutine 和线程的区别 Goroutine 是 Go 中并发执行函数或方法的方式。它比线程更轻量级,因为它的创建和销毁的代价更低。与线程相比,Goroutine 在运行时的栈空间要小得多,并且可以根据需要动态增长和缩小。同时,Go 运行时也负责在系统线程上调度所有 Goroutine 的执行,这使

    2024年02月15日
    浏览(37)
  • Golang八股文面试题

    TCP实现可靠传输依靠的有序列号,自动重传,滑动窗口,确认应答等机制

    2024年04月15日
    浏览(49)
  • 20个 Golang 常见面试问题

    1 Goroutine 在  Golang中的作用是什么? Goroutines 使得 Golang 能够并发执行多线程任务,允许函数在不相互阻塞的情况下同时运行。 2 如何在 Golang 中处理共享资源的并发访问? Golang 提供了同步原语,如互斥锁(mutexes)和通道(channels),用于在并发场景中安全地访问和修改共享

    2024年04月28日
    浏览(32)
  • 高频golang面试题:简单聊聊内存逃逸?

    知道golang的内存逃逸吗?什么情况下会发生内存逃逸? golang程序变量会携带有一组校验数据,用来证明它的整个生命周期是否在运行时完全可知。如果变量通过了这些校验,它就可以在栈上分配。否则就说它 逃逸 了,必须在堆上分配。 能引起变量逃逸到堆上的典型情况:

    2024年02月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包