
1、写出下面代码的输出结果,并给出原因和修正。
1 | func main() { |
解答:v 的地址是不会变化的,但是 goroutine 的运行又是不可预期的,当循环执行完后goroutine才开始执行,此时v的值为c, 然后3次goroutine都会打印c。
1 | c |
如果要输出 a b c得这样修正, goroutine 的时候将参数传入到匿名函数中,即使是这样,abc的顺序也是不可预期的,打印多次都是乱序。
1 | func main() { |
2、下面代码有问题吗?应该如何修复?
1 | var stringList []string |
slice非线程安全的,goroutine的运行不可预测,所以stringList里面是乱序且元素不一定是0~4的全集合
非线程安全的可以加锁,但是元素也不是有序的
1 | var stringList []string |
3、下面这段代码有什么输出?
1 | type People struct{} |
showA
showB
teacher showB
4、下面这段代码会输出什么,如何修复?
1 | func test0() { |
解答:
jsonBlob: {“name”: “Platypus”, “order”:”Monotremata”, “id”: 1}
animals: {id:0 name: order:}
反序列号的时候对应的结构体需要是导出的,将结构体改为导出字段即可
1 | func test0() { |
5、输出的结果为?为什么?
func Testlface() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
var (
data *int
eface interface{}
)
eface = data
fmt.Println(data == nil)
fmt.Println(eface == nil)
}
[0 0 0 0 0 1 2 3]
true
falseeface
不是 nil
的原因是它是一个接口类型,被赋值为 *int
类型的指针。即使指针 data
没有被初始化并且是 nil
,接口 eface
仍然持有一个 *int
类型的值,因此不是 nil
。当一个接口被赋值为特定类型的值时,它包含两个组件 - 值的类型和值的指针。在这种情况下,eface
持有一个 *int
类型的值,这意味着它包含一个指向整数值的指针(即使该指针当前为 nil
)
6、下面代码是否有错?为什么?
1 | func Testlface2() { |
不能将slice转为接口类型切片,应该直接转为接口类型就可以了,修改为
1 | func Testlface2() { |
7、下面的代码会有什么问题?
1 | func main() { |
读取关闭的chan会返回空值,所以输出0~9后会不断的输出0
8、下面代码会怎么样?为什么?
1 | func main() { |
这段代码会导致运行时错误,因为在 go 协程中创建的切片 s 没有被初始化,所以它的长度为 0,无法访问或设置其索引为 0 的元素。这会导致 panic,并且程序会崩溃。
会输出 panic: runtime error: index out of range [0] with length 0
9、假设有100个[]int切片,现在要计算这些切片元素的总和,并且要求最大只有10个goroutine同时在计算。
1 | func main() { |
10、给出LRU-Cache的伪代码(数据结构及大致思路)。cache支持协程安全,支持get、put操作,每次get、put更新key的存活时间。
以下是一个简单的LRU-Cache的伪代码,该cache支持协程安全,支持get、put操作,每次get、put更新key的存活时间。
数据结构:
- 双向链表:用于维护key的访问顺序,最近访问的key放在链表头部,最少访问的key放在链表尾部。
- 哈希表:用于快速查找key对应的节点。
伪代码:
1 | type Node struct { |
在这个伪代码中,我们首先定义了一个 Node
结构体,用于表示链表中的节点。每个节点包含一个 key
、一个 value
、一个 prev
指针、一个 next
指针和一个 time
时间戳,用于表示该节点的存活时间。
然后,我们定义了一个 LRUCache
结构体,用于表示LRU-Cache。每个 LRUCache
包含一个容量 capacity
、一个大小 size
、一个链表头部 head
、一个链表尾部 tail
、一个哈希表 cache
和一个互斥锁 mutex
,用于协程安全。
在 LRUCache
中,我们定义了 NewLRUCache
、Get
、Put
、addToHead
、removeNode
、moveToHead
和 removeTail
等方法。其中,NewLRUCache方法用于创建一个新的
LRUCache` 实例,需要传入一个容量参数。
Get
方法用于获取指定key对应的value。首先,我们使用互斥锁进行协程安全保护。然后,我们从哈希表中查找key对应的节点,如果找到了节点,则更新节点的存活时间,并将节点移动到链表头部,表示该节点是最近访问的节点。最后,返回节点的value和true。如果没有找到节点,则返回0和false。
Put
方法用于插入一个新的key-value对或更新一个已有的key的value。首先,我们使用互斥锁进行协程安全保护。然后,我们从哈希表中查找key对应的节点,如果找到了节点,则更新节点的存活时间和value,并将节点移动到链表头部,表示该节点是最近访问的节点。如果没有找到节点,则创建一个新的节点,并将节点加入到链表头部。如果插入新节点后超过了容量,则删除链表尾部的节点。最后,更新哈希表和大小。
addToHead
方法用于将指定节点插入到链表头部。
removeNode
方法用于从链表中删除指定节点。
moveToHead
方法用于将指定节点移动到链表头部。
removeTail
方法用于删除链表尾部的节点,并返回被删除的节点。
在这个伪代码中,我们使用了一个双向链表和一个哈希表来实现LRU-Cache。在链表中,最近访问的节点放在链表头部,最少访问的节点放在链表尾部。在哈希表中,key对应的value是链表中的节点。使用双向链表和哈希表可以实现快速的访问和插入/删除操作,时间复杂度为O(1)。同时,使用互斥锁可以保证并发访问的安全性。