Go的数据结构与实现【Set】

介绍

Set是值的集合,可以迭代这些值、添加新值、删除值并清除集合、获取集合大小并检查集合是否包含值,集合中的一个值只存储一次,不能重复。
本文代码地址为go-store

简单实现

这是集合的一个简单实现,还不是并发安全的,为了简单和容易理解而没有引入锁资源。
set.go

package settype T inttype Set struct {sets map[T]bool
}// Add adds a new element to the Set. Returns true if t is not in set.
func (s *Set) Add(t T) bool {if s.sets == nil {s.sets = make(map[T]bool)}_, ok := s.sets[t]if !ok {s.sets[t] = true}return !ok
}// Clear removes all elements from the Set
func (s *Set) Clear() {s.sets = make(map[T]bool)
}// Delete removes the Item from the Set and returns true if t is in set
func (s *Set) Delete(t T) bool {_, ok := s.sets[t]if ok {delete(s.sets, t)}return ok
}// Contains returns true if the Set contains the t
func (s *Set) Contains(t T) bool {_, ok := s.sets[t]return ok
}// All returns the all items stored
func (s *Set) All() []T {var ret []Tfor t := range s.sets {ret = append(ret, t)}return ret
}// Size returns the size of the set
func (s *Set) Size() int {return len(s.sets)
}

单元测试

这是上面代码的单元测试,它详细解释了如何使用它,以及任何操作的预期结果:

set_test.go

package setimport "testing"var (t1 T = 1t2 T = 2t3 T = 3
)func InitSet() *Set {set := &Set{}set.Add(t1)set.Add(t2)return set
}func TestSet_Add(t *testing.T) {set := InitSet()ok := set.Add(t1)if ok {t.Errorf("There is already %d in set!", t1)}ok = set.Add(t3)if !ok {t.Errorf("There should be %d in set!", t3)}
}func TestSet_Clear(t *testing.T) {set := InitSet()set.Clear()if size := set.Size(); size != 0 {t.Errorf("wrong count, expected 0 and got %d", size)}
}func TestSet_Delete(t *testing.T) {set := InitSet()ok := set.Delete(t1)if !ok {t.Errorf("There should be %d in set!", t1)}ok = set.Delete(t3)if ok {t.Errorf("There should not be %d in set!", t3)}
}func TestSet_Contains(t *testing.T) {set := InitSet()ok := set.Contains(t1)if !ok {t.Errorf("There should be %d in set!", t1)}ok = set.Contains(t2)if !ok {t.Errorf("There should be %d in set!", t2)}ok = set.Contains(t3)if ok {t.Errorf("There should not be %d in set!", t3)}
}func TestSet_All(t *testing.T) {set := InitSet()items := set.All()if len(items) != 2 {t.Errorf("wrong count, expected 2 and got %d", len(items))}if items[0] != t1 && items[1] != t2 {t.Errorf("There should be %d and %d in set!", t1, t2)}
}func TestSet_Size(t *testing.T) {set := InitSet()size := set.Size()if size != 2 {t.Errorf("wrong count, expected 2 and got %d", size)}set.Add(t3)size = set.Size()if size != 3 {t.Errorf("wrong count, expected 3 and got %d", size)}set.Delete(t3)size = set.Size()if size != 2 {t.Errorf("wrong count, expected 2 and got %d", size)}set.Delete(t2)size = set.Size()if size != 1 {t.Errorf("wrong count, expected 1 and got %d", size)}set.Delete(t1)size = set.Size()if size != 0 {t.Errorf("wrong count, expected 0 and got %d", size)}
}

并发安全

第一个版本不是并发安全的,因为一个goroutine可能将一个值添加到集合中,而另一个goroutine正在获取集合列表或大小。
以下代码在数据结构中添加了一个sync.RWMutex,使其并发安全,实现非常简单,只需要在每个方法中添加Lock()和defer UnLock()。上面的测试运行良好,无需对此实现进行任何修改。

package setimport ("sync"
)type T inttype Set struct {sync.RWMutexsets map[T]bool
}// Add adds a new element to the Set. Returns true if t is not in set.
func (s *Set) Add(t T) bool {s.Lock()defer s.Unlock()if s.sets == nil {s.sets = make(map[T]bool)}_, ok := s.sets[t]if !ok {s.sets[t] = true}return !ok
}// Clear removes all elements from the Set
func (s *Set) Clear() {s.Lock()defer s.Unlock()s.sets = make(map[T]bool)
}// Delete removes the Item from the Set and returns true if t is in set
func (s *Set) Delete(t T) bool {s.Lock()defer s.Unlock()_, ok := s.sets[t]if ok {delete(s.sets, t)}return ok
}// Contains returns true if the Set contains the t
func (s *Set) Contains(t T) bool {s.RLock()defer s.RUnlock()_, ok := s.sets[t]return ok
}// All returns the all items stored
func (s *Set) All() []T {s.RLock()defer s.RUnlock()var ret []Tfor t := range s.sets {ret = append(ret, t)}return ret
}// Size returns the size of the set
func (s *Set) Size() int {s.RLock()defer s.RUnlock()return len(s.sets)
}// Union returns a new set with elements from both
// the given sets
func (s *Set) Union(t *Set) *Set {ret := &Set{}s.RLock()for i := range s.sets {ret.sets[i] = true}s.RUnlock()t.RLock()for i := range t.sets {if _, ok := ret.sets[i]; !ok {ret.sets[i] = true}}t.RUnlock()return ret
}

添加更多集合操作

Set还可以通过实现一些常见的数学集合操作得到更多改进:并集、交集、差集和子集。

并集

在这里插入图片描述

// Union returns a new set with elements from both
// the given sets
func (s *Set) Union(t *Set) *Set {ret := &Set{}s.RLock()for i := range s.sets {ret.sets[i] = true}s.RUnlock()t.RLock()for i := range t.sets {if _, ok := ret.sets[i]; !ok {ret.sets[i] = true}}t.RUnlock()return ret
}

单元测试:

func TestSet_Union(t *testing.T) {set1 := InitSet(t1, t2)set2 := InitSet(t1, t3)set3 := set1.Union(set2)if len(set3.All()) != 3 {t.Errorf("wrong count, expected 3 and got %d", set3.Size())}//don't edit original setsif len(set1.All()) != 2 {t.Errorf("wrong count, expected 2 and got %d", set1.Size())}if len(set2.All()) != 2 {t.Errorf("wrong count, expected 2 and got %d", set2.Size())}
}

交集

在这里插入图片描述

func (s *Set) Intersection(t *Set) *Set {ret := &Set{}ret.sets = make(map[T]bool)s.RLock()t.RLock()defer s.RUnlock()defer t.RUnlock()for i := range t.sets {if _, ok := s.sets[i]; ok {ret.sets[i] = true}}return ret
}

单元测试:

func TestSet_Intersection(t *testing.T) {set1 := InitSet(t1, t2)set2 := InitSet(t1, t3)set3 := set1.Intersection(set2)if len(set3.All()) != 1 {t.Errorf("wrong count, expected 1 and got %d", set3.Size())}//don't edit original setsif len(set1.All()) != 2 {t.Errorf("wrong count, expected 2 and got %d", set1.Size())}if len(set2.All()) != 2 {t.Errorf("wrong count, expected 2 and got %d", set2.Size())}
}

差集

在这里插入图片描述

func (s *Set) Difference(t *Set) *Set {ret := &Set{}ret.sets = make(map[T]bool)s.RLock()t.RLock()defer s.RUnlock()defer t.RUnlock()for i := range t.sets {if _, ok := s.sets[i]; !ok {ret.sets[i] = true}}return ret
}

单元测试:

func TestSet_Difference(t *testing.T) {set1 := InitSet(t1, t2)set2 := InitSet(t1, t3)set3 := set1.Difference(set2)if len(set3.All()) != 1 {t.Errorf("wrong count, expected 1 and got %d", set3.Size())}//don't edit original setsif len(set1.All()) != 2 {t.Errorf("wrong count, expected 2 and got %d", set1.Size())}if len(set2.All()) != 2 {t.Errorf("wrong count, expected 2 and got %d", set2.Size())}
}

子集

在这里插入图片描述

func (s *Set) Subset(t *Set) bool {s.RLock()t.RLock()defer s.RUnlock()defer t.RUnlock()for i := range s.sets {if _, ok := t.sets[i]; !ok {return false}}return true
}

单元测试:

func TestSet_Subset(t *testing.T) {set1 := InitSet(t1, t2)set2 := InitSet(t1, t3)ret := set2.Subset(set1)if ret {t.Errorf("(t1, t2) is not the subset of (t1, t3), but got %t", ret)}set3 := InitSet(t1)ret = set3.Subset(set1)if !ret {t.Errorf("(t1) is the subset of (t1, t3), but got %t", ret)}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/573411.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 性能优化(六):启动优化的详细流程

书接上文,Android 性能优化(一):闪退、卡顿、耗电、APK 从用户体验角度有四个性能优化方向: 追求稳定,防止崩溃追求流畅,防止卡顿追求续航,防止耗损追求精简,防止臃肿 …

机器学习——聚类算法-KMeans聚类

机器学习——聚类算法-KMeans聚类 在机器学习中,聚类是一种无监督学习方法,用于将数据集中的样本划分为若干个簇,使得同一簇内的样本相似度高,不同簇之间的样本相似度低。KMeans聚类是一种常用的聚类算法之一,本文将介…

外包干了15天,技术退步明显。。。。。。

说一下自己的情况,本科生,19年通过校招进入武汉某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&a…

开源博客项目Blog .NET Core源码学习(12:App.Application项目结构分析)

开源博客项目Blog的App.Application项目主要定义网站页面使用的数据类,同时定义各类数据的增删改查操作接口和实现类。App.Application项目未安装Nuget包,主要引用App.Core项目的类型。   App.Application项目的顶层文件夹如下图所示,下面逐…

蓝桥杯刷题-分巧克力

分巧克力 二分: N, K map(int, input().split()) w, h [], [] for i in range(N):cur_w, cur_h map(int, input().split())w.append(cur_w)h.append(cur_h)# 判断是否能分成K个及以上的巧克力 def check(a) -> bool: num 0for i in range(N):num (w[i] // …

3月份的倒数第二个周末有感

坐在图书馆的那一刻,忽然感觉时间的节奏开始放缓。今天周末因为我们两都有任务需要完成,所以就选了嘉定图书馆,不得不说嘉定新城远香湖附近的图书馆真的很有感觉。然我不经意回想起学校的时光,那是多么美好且短暂的时光。凝视着窗…

MySQL---事务

目录 一、事务简介 二、事务操作 1.未控制事务 2.事务控制一 3.控制事务二 三、事务的四大特性 四、并发事务问题 五、事务隔离级别 一、事务简介 事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或…

Android-Handler详解_使用篇

本文我将从Handler是什么、有什么、怎们用、啥原理,四个方面去分析。才疏学浅,如有错误,欢迎指正,多谢。 1.是什么 因为Android系统不允许在子线程访问UI组件,否则就会抛出异常。所以咱们平实用的最多的可能是在子线…

Leetcode239_滑动窗口最大值

1.leetcode原题链接:. - 力扣(LeetCode) 2.题目描述 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口…

python实战之宝塔部署flask项目

一. 项目 这个demo只是提供了简单的几个api接口, 并没有前端页面 # -*- coding: utf-8 -*- import flask as fk from flask import jsonify, requestapp fk.Flask(__name__)app.route(/api/hello, methods[GET]) def get_data():return hello world# 假设我们要提供一个获取用…

Vue3气泡卡片(Popover)

效果如下图:在线预览 APIs 参数说明类型默认值必传title卡片标题string | slot‘’falsecontent卡片内容string | slot‘’falsemaxWidth卡片内容最大宽度string | number‘auto’falsetrigger卡片触发方式‘hover’ | ‘click’‘hover’falseoverlayStyle卡片样式…

CSS(六)

一、精灵图 1.1 为什么需要精灵图 一个网页中往往会应用很多小的背景图像作为修饰,当网页中的图像过多时,服务器就会频繁地接收和发送请求图片,造成服务器请求压力过大,这将大大降低页面的加载速度。 因此,为了有效…