golang 空结构体 struct{}
2022-05-21 23:22 youxin 阅读(1021) 评论(0) 编辑 收藏 举报
定义的各种姿势
原生定义
a := struct{}{}
1
struct{} 可以就认为是一种类型,a 变量就是 struct {} 类型的一种变量,地址为 runtime.zerobase ,大小为 0 ,不占内存。
重定义类型
golang 使用 type 关键字定义新的类型,比如:
type emptyStruct struct{}
1
定义出来的 emptyStruct 是新的类型,具有对应的 type 结构,但是性质 struct{} 完全一致,编译器对于 emptryStruct 类型的内存分配,也是直接给 zerobase 地址的。
————————————————
var s struct{}
变量 size 是 0 ; fmt.Println(unsafe.Sizeof(s))
本质上来讲,使用空结构体的初衷只有一个:节省内存,但是更多的情况,节省的内存其实很有限,这种情况使用空结构体的考量其实是:根本不关心结构体变量的值。
作用1 :做set,map[string]struct{}
由于struct{}是空,不关心内容,这样map便改造为set
set := make(map[string]struct{}) for _, value := range []string{"apple", "orange", "apple"} { set[value] = struct{}{} } fmt.Println(set) // Output: map[orange:{} apple:{}]
map 和 struct {} 一般的结合姿势是这样的:
// 创建 map
m := make(map[int]struct{})
// 赋值
m[1] = struct{}{}
// 判断 key 键存不存在
_, ok := m[1]
一般 map 和 struct {} 的结合使用场景是:只关心 key,不关注值。比如查询 key 是否存在就可以用这个数据结构,通过 ok 的值来判断这个键是否存在,map 的查询复杂度是 O(1) 的,查询很快。
你当然可以用 map[int]bool 这种类型来代替,功能也一样能实现,很多人考虑使用 map[int]struct{} 这种使用方式真的就是为了省点内存,当然大部分情况下,这种节省是不足道哉的,所以究竟要不要这样使用还是要看具体场景。
作用2:chan struct{}
可以做退出
func worker(i int, ch chan Work, quit chan struct{}) { for { select { case w := <-ch: // do something case <-quit: fmt.Println("worker", i, "quitting") quit = nil } } }
还有其他情况,比如有时候使用channel,但并不需要附带任何数据。
func worker(ch chan struct{}) { // Receive a message from the main program. <-ch println("roger") // Send a message to the main program. close(ch) } func main() { ch := make(chan struct{}) go worker(ch) // Send a message to a worker. ch <- struct{}{} // Receive a message from the worker. <-ch println(“roger") // Output: // roger // roger }
chan & struct{}
channel 和 struct{} 结合是一个最经典的场景,struct{} 通常作为一个信号来传输,并不关注其中内容。chan 的分析在前几篇文章有详细说明。chan 本质的数据结构是一个管理结构加上一个 ringbuffer ,如果 struct{} 作为元素的话,ringbuffer 就是 0 分配的。
chan 和 struct{} 结合基本只有一种用法,就是信号传递,空结构体本身携带不了值,所以也只有这一种用法啦,一般来说,配合 no buffer 的 channel 使用。
// 创建一个信号通道 waitc := make(chan struct{}) // ... goroutine 1: // 发送信号: 投递元素 waitc <- struct{} // 发送信号: 关闭 close(waitc) goroutine 2: select { // 收到信号,做出对应的动作 case <-waitc: }
这种场景我们思考下,是否一定是非 struct{}
不可?其实不是,而且也不多这几个字节的内存,所以这种情况真的就只是不关心 chan
的元素值而已,所以才用的 struct{}
。
原文链接:https://blog.csdn.net/qiya2007/article/details/111502649
https://blog.csdn.net/qiya2007/article/details/111502649
https://blog.aflybird.cn/2022/02/%E8%B0%88%E8%B0%88go%E8%AF%AD%E8%A8%80zerobase/