Golang 使用Map构建Set类型的实现方法
前言
本篇主要给大家讲述了如何利用Go语言的语法特性实现Set类型的数据结构,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍吧。
需求
对于Set类型的数据结构,其实本质上跟List没什么多大的区别。无非是Set不能含有重复的Item的特性,Set有初始化、Add、Clear、Remove、Contains等操作。接下来看具体的实现方式分析吧。
实现
仍然按照已有的编程经验来联想如何实现基本Set功能,在Java中很容易知道HashSet的底层实现是HashMap,核心的就是用一个常量来填充Map键值对中的Value选项。除此之外,重点关注Go中Map的数据结构,Key是不允许重复的,如下所示:
|
1
2
3
4
5
6
7
|
m := map[string]string{ "1": "one", "2": "two", "1": "one", "3": "three", } fmt.Println(m) |
程序会直接报错,提示重复Key值,这样就非常符合Set的特性需求了。
定义
前面分析出Set的Value为固定的值,用一个常量替代即可。但是笔者分析的实现源码,用的是一个空结构体来实现的,如下所示:
|
1
2
3
4
5
6
7
|
// 空结构体var Exists = struct{}{}// Set is the main interfacetype Set struct { // struct为结构体类型的变量 m map[interface{}]struct{}} |
为了解决上面为什么用空结构体来做常量Value,先看下面的是测试:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import ( "fmt" "unsafe")// 定义非空结构体type S struct { a uint16 b uint32}func main() { var s S fmt.Println(unsafe.Sizeof(s)) // prints 8, not 6 var s2 struct{} fmt.Println(unsafe.Sizeof(s2)) // prints 0} |
打印出空结构体变量的内存占用大小为0,再看看下面这个测试:
|
1
2
3
4
|
a := struct{}{}b := struct{}{}fmt.Println(a == b) // truefmt.Printf("%p, %p\n", &a, &b) // 0x55a988, 0x55a988 |
很有趣,a和b竟然相等,并且a和b的地址也是一样的。现在各位应该明白了为什么会有:
|
1
|
var Exists = struct{}{} |
这样的常量也来填充所有Map的Value了吧,Go真是精彩!!!
初始化
Set类型数据结构的初始化操作,在声明的同时可以选择传入或者不传入进去。声明Map切片的时候,Key可以为任意类型的数据,用空接口来实现即可。Value的话按照上面的分析,用空结构体即可:
|
1
2
3
4
5
6
7
8
|
func New(items ...interface{}) *Set { // 获取Set的地址 s := &Set{} // 声明map类型的数据结构 s.m = make(map[interface{}]struct{}) s.Add(items...) return s} |
添加
简化操作可以添加不定个数的元素进入到Set中,用变长参数的特性来实现这个需求即可,因为Map不允许Key值相同,所以不必有排重操作。同时将Value数值指定为空结构体类型。
|
1
2
3
4
5
6
|
func (s *Set) Add(items ...interface{}) error { for _, item := range items { s.m[item] = Exists } return nil} |
包含
Contains操作其实就是查询操作,看看有没有对应的Item存在,可以利用Map的特性来实现,但是由于不需要Value的数值,所以可以用 _,ok来达到目的:
|
1
2
3
4
|
func (s *Set) Contains(item interface{}) bool { _, ok := s.m[item] return ok} |
长度和清除
获取Set长度很简单,只需要获取底层实现的Map的长度即可:
|
1
2
3
|
func (s *Set) Size() int { return len(s.m)} |
清除操作的话,可以通过重新初始化Set来实现,如下即为实现过程:
|
1
2
3
|
func (s *Set) Clear() { s.m = make(map[interface{}]struct{})} |
相等
判断两个Set是否相等,可以通过循环遍历来实现,即将A中的每一个元素,查询在B中是否存在,只要有一个不存在,A和B就不相等,实现方式如下所示:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
func (s *Set) Equal(other *Set) bool { // 如果两者Size不相等,就不用比较了 if s.Size() != other.Size() { return false } // 迭代查询遍历 for key := range s.m { // 只要有一个不存在就返回false if !other.Contains(key) { return false } } return true} |
子集
判断A是不是B的子集,也是循环遍历的过程,具体分析在上面已经讲述过,实现方式如下所示:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
func (s *Set) IsSubset(other *Set) bool { // s的size长于other,不用说了 if s.Size() > other.Size() { return false } // 迭代遍历 for key := range s.m { if !other.Contains(key) { return false } } return true} |
Ok,以上就是Go中Set的主要函数实现方式,还是很有意思的。继续加油。
Golang 使用Map构建Set类型的实现方法的更多相关文章
- golang中map并发读写问题及解决方法
一.map并发读写问题 如果map由多协程同时读和写就会出现 fatal error:concurrent map read and map write的错误 如下代码很容易就出现map并发读写问题 ...
- 工作随笔——Golang interface 转换成其他类型
新的公司,新的氛围.一年了,打算写点什么.so,那就写google的golang语言吧. 最最最基础的语法结构见go语言菜鸟教程 接下来写点菜鸟教程没有的. go语言的设计者认为:go语言必须让程序员 ...
- 总结golang之map
总结golang之map 2017年04月13日 23:35:53 趁年轻造起来 阅读数:18637 标签: golangmapgo 更多 个人分类: golang 版权声明:本文为博主原创文章, ...
- golang之map的使用声明
1.map的基本介绍 map是key-value数据结构,又称为字段或者关联数组.类似其它编程语言的集合,在编程中是经常使用到的 2.map的声明 1)基本语法 var map 变量名 map[key ...
- webservice返回值为Map类型的处理方法
在写一个webservice的时候,方法的返回值是一个复杂类型,处理方法是写一个结果类(Javabean)作为返回值.想着webservice方法返回值为Map的没写过,然后就试着写了一个简单的Dem ...
- Map和String类型之间的转换
前提是String的格式是map或json类型的 public static void main(String[] args) { Map<String,Object> map = new ...
- Golang中map的三种声明方式和简单实现增删改查
package main import ( "fmt" ) func main() { test3 := map[string]string{ "one": & ...
- golang 中 map 转 struct
golang 中 map 转 struct package main import ( "fmt" "github.com/goinggo/mapstructure&qu ...
- sql.Rows 转换为 []map[string]interface{} 类型
// *sql.Rows 转换为 []map[string]interface{}类型 func rows2maps(rows *sql.Rows) (res []map[string]interfa ...
随机推荐
- C/C++的64位整型
在C/C++中,64为整型一直是一种没有确定规范的数据类型.现今主流的编译器中,对64为整型的支持也是标准不一,形态各异.一般来说,64位整型的定义方式有long long和__int64两种(VC还 ...
- input onchange事件
//输入帐号,默认赋值给员工电话和后台登录帐号$('#mobile_phone_').bind('input propertychange', function() { var phone = $(' ...
- 一个洛谷Material化的Stylish美化主题
Luogu Argon Design 新主题 Luogu Argon Design 目前已经取代了 Luogu Material,但这并不代表 Luogu Material 会停止更新,在 Luogu ...
- LINUX的STRACE命令用法 [转]
调用:strace [ -dffhiqrtttTvxx ] [ -acolumn ] [ -eexpr ] ...[ -ofile ] [ -ppid ] ... [ -sstrsize ] [ -u ...
- Qt_mingw搭建opencv开发环境
Qt在windows下共有2个版本:mingw和msvc.其中mingw使用gcc编译器,msvc使用微软的VC编译器.针对不同版本Qt,使用Opencv的方式也不同. 区别 msvc, 可以使用op ...
- docker容器重启故障
问题 强杀docker进程后,重启docker.docker中的容器无法启动并报错,报错内容如下 docker restart ae1f7b2c2f15 Error response from dae ...
- Python学习笔记之函数式编程
python中的高阶函数 高阶函数就是 变量名指向函数,下面代码中的变量abs其实是一个函数,返回数字的绝对值,如abs(-10) 返回 10 def add(x,y,f): return f(x) ...
- C向C++改造
步骤: 1. 把c文件后缀名换成cpp2. Android.mk文件中的hello.c也要换成hello.cpp3. c++的使用的环境变量结构体中,访问了c使用的结构体的函数指针,函数名全部都是一样 ...
- 深入理解Git - 一切皆commit
在对 git 有了基本理解和知道常规操作之后,如何对 git 的使用有进一步的理解? 一切皆 commit 或许是个不错的理解思路. 本文将从『一切皆 commit 』的角度,通过 git 中常见的名 ...
- ARP协议详解之ARP动态与静态条目的生命周期
ARP协议详解之ARP动态与静态条目的生命周期 ARP动态条目的生命周期 动态条目随时间推移自动添加和删除. q 每个动态ARP缓存条目默认的生命周期是两分钟.当超过两分钟,该条目会被删掉.所以,生 ...