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 ...
随机推荐
- Oracle学习笔记:外连接(+)的用法
Oracle中常用 left join 和 right join 来进行外连接,同时,oracle也支持 (+) 的特殊用法,也是表示外连接,并且总是放在非主表的一方. 例如: 左外连接: selec ...
- django中的view测试和models测试样例
感觉用model_mommy比factory_boy要好些. 如果Models.py如下: from django.db import models from django.contrib.auth. ...
- Jupyter Notebook 入门
参考 Jupyter Notebook 快速入门 进阶 可看: Jupyter Notebook 的 27 个窍门,技巧和快捷键 Jupyter Notebook(此前被称为 IPython ...
- 013 jquery中关于表格行的增删问题
1.程序 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <titl ...
- chrome使用技巧(转)
原文:http://www.cnblogs.com/liyunhua/p/4544738.html 阅读目录 写在前面 快速切换文件 在源代码中搜索 在源代码中快速跳转到指定的行 使用多个插入符进行选 ...
- Java Socket实战之一 单线程通信基础socket
现在做Java直接使用Socket的情况是越来越少,因为有很多的选择可选,比如说可以用spring,其中就可以支持很多种远程连接的操作,另外jboss的remoting也是不错的选择,还有Apache ...
- 深入理解Git - 一切皆commit
在对 git 有了基本理解和知道常规操作之后,如何对 git 的使用有进一步的理解? 一切皆 commit 或许是个不错的理解思路. 本文将从『一切皆 commit 』的角度,通过 git 中常见的名 ...
- canvas入门级小游戏《开关灯》思路讲解
游戏很简单,10行10列布局,每行每列各10盏灯,游戏初始化时随机点亮其中一些灯,点击某盏灯,其上下左右的灯及本身状态反转,如果点击前是灭着的,点击后即点亮,将所有灯全部点亮才算过关.游戏试玩: 下面 ...
- 无线网卡服务端工具airserv-ng
无线网卡服务端工具airserv-ng 由于WiFi信号强度的限制,渗透测试人员只能监听主机周边范围的无线信号.为了解决这个问题,aircrack-ng套件提供了一个无线网卡服务端工具airser ...
- 【tarjan+SPFA】BZOJ1179-[Apio2009]Atm
[题目大意] 给出一张有点权的有向图,已知起点和可以作为终点的一些点,问由起点出发,每条边和每个点可以经过任意多次,经过点的权值总和最大为多少. [思路] 由于可以走任意多次,显然强连通分量可以缩点. ...