Go 不可重复协程安全队列

代码实现

package dataStruct

import (
	"errors"
	"sync"
)

// GenericQueue 是一个支持泛型的不可重复队列,具有最大长度限制
// T 是泛型参数
type GenericQueue[T comparable] struct {
	items   map[T]struct{} // 使用 map 来存储元素
	order   []T            // 使用切片来记录元素的顺序
	lock    sync.RWMutex   // 读写锁,保证并发安全
	maxSize int            // 最大队列长度
}

// NewGenericQueue 创建一个新的泛型队列,并指定最大长度
func NewGenericQueue[T comparable](maxSize int) *GenericQueue[T] {
	return &GenericQueue[T]{
		items:   make(map[T]struct{}), // 初始化 items map
		order:   []T{},                // 初始化 order 切片
		maxSize: maxSize,              // 设置最大长度
	}
}

// Add 添加元素到队列
// 如果队列已满,默认移除最旧的元素来腾出空间
func (q *GenericQueue[T]) Add(item T) error {
	q.lock.Lock()         // 加写锁,防止并发修改
	defer q.lock.Unlock() // 解锁

	// 判断元素是否已经存在
	if _, exists := q.items[item]; exists {
		return errors.New("item already exists in the queue")
	}

	// 如果队列已满,移除最旧的元素
	if len(q.order) >= q.maxSize {
		return errors.New("Out of length Queue ")
	}

	// 将元素添加到 map 中,标记该元素已存在
	q.items[item] = struct{}{}
	// 将元素添加到队列顺序中
	q.order = append(q.order, item)
	return nil
}

// Remove 删除队列中的指定元素
// 如果元素不存在,则返回错误
func (q *GenericQueue[T]) Remove(item T) error {
	q.lock.Lock()         // 加写锁,防止并发修改
	defer q.lock.Unlock() // 解锁

	// 判断元素是否存在
	if _, exists := q.items[item]; !exists {
		return errors.New("item not found in the queue")
	}

	// 从 map 中删除该元素
	delete(q.items, item)

	// 从顺序切片中删除该元素
	for i, v := range q.order {
		if v == item {
			q.order = append(q.order[:i], q.order[i+1:]...) // 删除指定位置的元素
			break
		}
	}
	return nil
}

// Update 修改队列中的元素
// 如果元素不存在,则返回错误
func (q *GenericQueue[T]) Update(oldItem, newItem T) error {
	q.lock.Lock()         // 加写锁,防止并发修改
	defer q.lock.Unlock() // 解锁

	// 判断 oldItem 是否存在
	if _, exists := q.items[oldItem]; !exists {
		return errors.New("old item not found in the queue")
	}

	// 判断 newItem 是否已存在
	if _, exists := q.items[newItem]; exists {
		return errors.New("new item already exists in the queue")
	}

	// 从 map 中删除 oldItem,并添加 newItem
	delete(q.items, oldItem)
	q.items[newItem] = struct{}{}

	// 更新顺序切片中的 oldItem 为 newItem
	for i, v := range q.order {
		if v == oldItem {
			q.order[i] = newItem
			break
		}
	}
	return nil
}

// Contains 查询队列中是否存在指定元素
func (q *GenericQueue[T]) Contains(item T) bool {
	q.lock.RLock()         // 加读锁,防止并发修改
	defer q.lock.RUnlock() // 解锁
	_, exists := q.items[item]
	return exists
}

// Traverse 遍历队列中的元素
func (q *GenericQueue[T]) Traverse() []T {
	q.lock.RLock()         // 加读锁,防止并发修改
	defer q.lock.RUnlock() // 解锁
	// 返回元素的副本,防止并发问题
	result := make([]T, len(q.order))
	copy(result, q.order)
	return result
}

// Iterator 返回一个可以用于 for range 的通道
func (q *GenericQueue[T]) Iterator() <-chan T {
	ch := make(chan T)
	go func() {
		q.lock.RLock()         // 加读锁,防止并发修改
		defer q.lock.RUnlock() // 解锁
		defer close(ch)        // 关闭通道
		for _, item := range q.order {
			ch <- item
		}
	}()
	return ch
}

介绍

1. 支持泛型

  • 队列支持泛型参数 T,因此可以存储任何类型的元素,只要该类型是 comparable(可比较的)。这意味着你可以使用这个队列来存储数字、字符串、结构体等类型的数据。

2. 不可重复

  • 队列内部通过 map 来存储元素,因此它自动确保元素的唯一性。也就是说,队列中不会有重复的元素。

3. 顺序存储

  • 使用切片 order 来记录元素的顺序,保证在遍历时元素的顺序是加入队列时的顺序。

4. 并发安全

  • 队列实现了并发控制,使用了 sync.RWMutex 读写锁来保证在多线程环境下的安全操作。多个 goroutine 可以并发地读取队列,或者一个 goroutine 可以安全地修改队列。

5. 常见操作

  • 添加元素(Add): 如果元素已经存在,则无法再次添加;如果队列已满,返回 Out of length Queue 错误,不会自动移除最旧元素
  • 删除元素(Remove): 可以删除队列中的某个元素。
  • 更新元素(Update): 可以将队列中的某个元素更新为新的元素,前提是新元素不存在。
  • 检查元素是否存在(Contains): 查询队列中是否包含某个元素。
  • 遍历队列(Traverse): 返回队列中所有元素的副本,供遍历使用。
  • 支持 for range 遍历: 通过 Iterator 方法,队列可以返回一个只读通道,可以直接在 for range 循环中使用。

6. 适用场景

  • 适用于需要按顺序处理且确保元素唯一的场景,例如任务队列、事件队列等。
  • 适用于并发环境,确保多个操作的安全性。

示例用法:

 

type Ta struct {
	Name string
}

func main() {
	impl := test.XueTestingImpl
	impl.StartTest()

	// 示例:创建一个支持整数的泛型队列
	queue := dataStruct.NewGenericQueue[Ta](1024)
	queue.Add(Ta{Name: "xzm"})
	queue.Add(Ta{Name: "yyds"})
	queue.Add(Ta{Name: "id"})

	//修改
	queue.Update(Ta{Name: "id"}, Ta{Name: "ids"})

	//遍历
	for ta := range queue.Iterator() {
		fmt.Println(ta)
	}

	//查找
	contains := queue.Contains(Ta{
		Name: "yyds",
	})

	//删除
	queue.Remove(Ta{
		Name: "yyds",
	})

	fmt.Println(contains)

	fmt.Println(queue)
	fmt.Println(queue.Traverse())
	impl.EndTest()
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Redis】Redis入门以及什么是分布式系统{Redis引入+分布式系统介绍}

文章目录 介绍redis的引入 分布式系统单机架构应用服务和数据库服务分离【负载均衡】引入更多的应用服务器节点 单机架构 分布式是什么 数据库分离和负载均衡 理解负载均衡 数据库读写分离 引入缓存 数据库分库分表 引入微服务 介绍 The open source, in-memory data store us…

线上内存泄漏排查思路

“内存泄漏”是开发者最害怕的问题之一&#xff0c;尤其是在高并发、高负载的线上环境中。它往往不易察觉&#xff0c;却能悄悄吞噬系统的性能&#xff0c;最终导致应用崩溃或响应变慢。你是否曾在项目上线后遇到过性能下降或宕机的问题&#xff0c;而问题根源竟然是内存泄漏&a…

【Redis】在ubuntu上安装Redis

文章目录 提权搜索软件包安装修改配置文件ip保护模式配置密码 重新启动服务器使用 redis 自带的客户端来连接服务器 提权 先切换到 root 用户,su 命令切换到 root. 搜索软件包 使用 apt 命令来搜索 redis 相关的软件包 apt search redis 安装 使用 apt 命令安装 redisapt …

人形机器人,自动驾驶“老炮”创业第二站

造一台人形机器人&#xff0c;或许正在成为2025年最炙手可热的事情。 从去年第四季度开始&#xff0c;伴随着大模型应用的深入&#xff0c;具身智能概念被点燃&#xff0c;其中最鲜明的一个特点是&#xff0c;大量自动驾驶大佬的转行加入。 随便说几个比较有分量的&#xff0…

《SwinIR:使用Swin-Transformer图像恢复》学习笔记

paper&#xff1a;2108.10257 GitHub&#xff1a;GitHub - JingyunLiang/SwinIR&#xff1a; SwinIR&#xff1a; 使用 Swin Transformer 进行图像修复 &#xff08;官方仓库&#xff09; 目录 摘要 1、Introduction 2、Related Work 2.1 图像修复 2.2 视觉Transformer…

力扣hot100-->滑动窗口、贪心

你好呀&#xff0c;欢迎来到 Dong雨 的技术小栈 &#x1f331; 在这里&#xff0c;我们一同探索代码的奥秘&#xff0c;感受技术的魅力 ✨。 &#x1f449; 我的小世界&#xff1a;Dong雨 &#x1f4cc; 分享我的学习旅程 &#x1f6e0;️ 提供贴心的实用工具 &#x1f4a1; 记…

Top 30的AI应用产品出海经验分享

榜单说明 本文基于对AI图片产品的分类和流量分析&#xff0c;旨在洞察AI图片应用的出海趋势。以下是分类和收录标准&#xff1a; 分类标准 将AI图片产品分为三大类&#xff1a;图片生成、图片编辑和平面设计。 图片生成&#xff1a;以基于大模型生成图片并展示结果&#xff0…

Hive之加载csv格式数据到hive

场景&#xff1a; 今天接了一个需求&#xff0c;将测试环境的hive数据导入到正式环境中。但是不需要整个流程的迁移&#xff0c;只需要迁移ads表 解决方案&#xff1a; 拿到这个需求首先想到两个方案&#xff1a; 1、将数据通过insert into语句导出&#xff0c;然后运行脚本 …

73,【5】BUUCTF WEB [网鼎杯 2020 玄武组]SSRFMe(未解出)

进入靶场 又是代码又是代码又是代码又是代码又是代码又是代码又是代码又是代码又是代码又是代码又是代码又是代码又是代码又是代码 <?php // 检查 URL 是否为内部 IP 地址 function check_inner_ip($url) {// 使用正则表达式检查 URL 格式是否以 http、https、gopher 或 d…

如何实现各种类型的进度条

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了浮动按钮相关的内容&#xff0c;,本章回中将介绍进度条相关的Widget,闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 进度条是常用的组件之一&#xff0c;它主要用来显示某种动作的完成进度。Flu…

复位信号的同步与释放(同步复位、异步复位、异步复位同步释放)

文章目录 背景前言一、复位信号的同步与释放1.1 同步复位1.1.1 综述1.1.2 优缺点 1.2 recovery time和removal time1.3 异步复位1.3.1 综述1.3.2 优缺点 1.4 同步复位 与 异步复位1.5 异步复位、同步释放1.5.1 总述1.5.2 机理1.5.3 复位网络 二、思考与补充2.1 复…

欢迎来到linux大陆!本次试炼地点——秩序“权限”圣殿

一篇关于权限的学习笔记~ 1、权限的概念2、权限管理2.1 角色的不同分类2.2 文件权限原理剖析2.2.1 熟悉指令2.2.2 普通用户只能更改自己的文件权限&#xff0c;但是sudo提权可以更改其他文件权限2.2.3 没有权限&#xff0c;系统拒绝访问2.2.4 权限匹配2.2.5 root用户不受任何限…

Spring 定时任务:@Scheduled 注解四大参数解析

本文主要介绍了在 Spring 框架中使用Scheduled注解实现定时任务的方法&#xff0c;重点讲解了fixedRate、fixedDelay、cron和initialDelay这四个参数的用法&#xff0c;并通过实例代码进行了详细说明。 1. fixedRate 参数 参数含义 fixedRate指定任务固定时间间隔执行。如设…

使用频谱仪:测量宽带信号的功率

marker默认只测一个频率点的功率&#xff0c;当测试宽带信号&#xff0c;如20MHz&#xff0c;不能直接使用marker来测量功率。 有2种方式&#xff1a; 宽带信号需要使用Measure-> channel power 来测量。 meas setup integ BW&#xff1a;500mhz Freq&#xff1a;中心频…

postman请求参数化

postman界面介绍 一、使用环境变量(Environment Variables)进行参数化 1、在请求中使用环境变量 在请求的url、请求头(Headers)、请求体(Body)等部分都可以使用环境变量。 URL 部分示例 点击 Postman 界面右上角的 “眼睛” 图标(Environment Quick Look)打开环境管理…

优选算法——哈希表

目录 1. 哈希表简介 2. 两数之和 3. 判定是否为字符重排 4. 存在重复元素 5. 字母异位词分组 1. 哈希表简介 2. 两数之和 题目链接&#xff1a;1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 题目展示&#xff1a; 题目分析&#xff1a; 大家来看上面的图&…

【C语言学习】:C语言补充:转义字符,<<,>>操作符,IDE

&#x1f381;个人主页&#xff1a;我们的五年https://blog.csdn.net/djdjiejsn?typeblog &#x1f50d;系列专栏&#xff1a;C课程学习https://blog.csdn.net/djdjiejsn/category_12617142.html &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 C语言学…

Cesium特效——城市白模的科技动效的各种效果

最终效果图如下&#xff1a; 实现方法&#xff1a; 步骤一&#xff1a;使用cesiumlib生产白模&#xff0c;格式为3dtiles 注意事项&#xff1a;采用其他方式可能导致白模贴地&#xff0c;从而导致不能实现该效果&#xff0c;例如把步骤二的服务地址改为Cesium Sandcastle 里的…

仿 RabbitMQ 的消息队列3(实战项目)

七. 消息存储设计 上一篇博客已经将消息统计文件的读写代码实现了&#xff0c;下一步我们将实现创建队列文件和目录。 实现创建队列文件和目录 初始化 0\t0 这样的初始值. //创建队列对应的文件和目录&#xff1a;public void createQueueFile(String queueName) throws IO…

无人机 PX4 飞控 | PX4源码添加自定义参数方法并用QGC显示与调整

无人机 PX4 飞控 | PX4源码添加自定义参数方法并用QGC显示与调整 0 前言 之前文章添加了一个自定义的模块&#xff0c;本篇文章在之前的自定义模块中&#xff0c;添加两个自定义参数 使用QGC显示出来&#xff0c;并通过QGC调整参数值&#xff0c;代码实现参数更新 新增的参…