using System.Collections; using System.Diagnostics; namespace Masuit.Tools.Systems; /// /// 一个只允许唯一元素的队列,结合了Queue和HashSet的特性 /// /// 队列中元素的类型 [DebuggerDisplay("Count = {Count}")] public class HashQueue : IReadOnlyCollection, ICollection, ICollection { private readonly Queue _queue; private readonly HashSet _hashSet; private readonly IEqualityComparer _comparer; #region 构造函数 /// /// 初始化 UniqueQueue 类的新实例 /// public HashQueue() : this((IEnumerable)null, null) { } /// /// 初始化 UniqueQueue 类的新实例,使用指定的相等比较器 /// /// 用于比较值的 IEqualityComparer public HashQueue(IEqualityComparer comparer) : this(null, comparer) { } /// /// 初始化 UniqueQueue 类的新实例,包含从指定集合复制的元素 /// /// 要复制到 UniqueQueue 中的集合 public HashQueue(IEnumerable collection) : this(collection, null) { } /// /// 初始化 UniqueQueue 类的新实例,包含从指定集合复制的元素,并使用指定的相等比较器 /// /// 要复制到 UniqueQueue 中的集合 /// 用于比较值的 IEqualityComparer public HashQueue(IEnumerable collection, IEqualityComparer comparer) { _comparer = comparer ?? EqualityComparer.Default; _hashSet = new HashSet(_comparer); _queue = new Queue(); if (collection != null) { foreach (var item in collection) { Enqueue(item); } } } /// /// 初始化 UniqueQueue 类的新实例,具有指定的初始容量 /// /// UniqueQueue 可包含的初始元素数目 public HashQueue(int capacity) : this(capacity, null) { } /// /// 初始化 UniqueQueue 类的新实例,具有指定的初始容量和使用指定的相等比较器 /// /// UniqueQueue 可包含的初始元素数目 /// 用于比较值的 IEqualityComparer public HashQueue(int capacity, IEqualityComparer comparer) { if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), "容量不能为负数。"); _comparer = comparer ?? EqualityComparer.Default; _hashSet = new HashSet(_comparer); _queue = new Queue(capacity); } #endregion 构造函数 #region 核心方法 /// /// 将对象添加到 UniqueQueue 的结尾处 /// /// 要添加到 UniqueQueue 的对象 /// 如果元素成功添加返回 true,如果元素已存在则返回 false public bool Enqueue(T item) { if (_hashSet.Add(item)) { _queue.Enqueue(item); return true; } return false; } /// /// 移除并返回位于 UniqueQueue 开始处的对象 /// /// 从 UniqueQueue 的开头移除的对象 /// UniqueQueue 为空 public T Dequeue() { if (_queue.Count == 0) throw new InvalidOperationException("队列为空。"); var item = _queue.Dequeue(); _hashSet.Remove(item); return item; } /// /// 尝试移除并返回位于 UniqueQueue 开始处的对象 /// /// 如果操作成功,则返回已移除的对象;否则为 T 的默认值 /// 如果成功移除并返回了对象,则为 true;否则为 false public bool TryDequeue(out T result) { if (_queue.Count > 0) { result = _queue.Dequeue(); _hashSet.Remove(result); return true; } result = default(T); return false; } /// /// 返回位于 UniqueQueue 开始处的对象但不将其移除 /// /// 位于 UniqueQueue 开头的对象 /// UniqueQueue 为空 public T Peek() { if (_queue.Count == 0) throw new InvalidOperationException("队列为空。"); return _queue.Peek(); } /// /// 返回位于 UniqueQueue 开始处的对象但不将其移除 /// /// 如果操作成功,则返回开头的对象;否则为 T 的默认值 /// 如果成功返回了对象,则为 true;否则为 false public bool TryPeek(out T result) { if (_queue.Count > 0) { result = _queue.Peek(); return true; } result = default(T); return false; } /// /// 确定某元素是否在 UniqueQueue 中 /// /// 要在 UniqueQueue 中定位的对象 /// 如果在 UniqueQueue 中找到 item,则为 true;否则为 false public bool Contains(T item) { return _hashSet.Contains(item); } /// Adds an item to the . /// The object to add to the . /// The is read-only. public void Add(T item) { Enqueue(item); } /// /// 从 UniqueQueue 中移除所有对象 /// public void Clear() { _queue.Clear(); _hashSet.Clear(); } #endregion 核心方法 #region 属性和状态 /// /// 获取 UniqueQueue 中包含的元素数 /// public int Count => _queue.Count; /// Gets a value indicating whether the is read-only. /// true if the is read-only; otherwise, false. public bool IsReadOnly { get; } /// /// 获取一个值,该值指示 UniqueQueue 是否为空 /// public bool IsEmpty => _queue.Count == 0; /// /// 获取用于确定集合中值是否相等的 IEqualityComparer /// public IEqualityComparer Comparer => _comparer; #endregion 属性和状态 #region 集合操作 /// /// 从 UniqueQueue 中移除指定项 /// /// 要移除的项 /// 如果成功找到并移除该项,则为 true;否则为 false public bool Remove(T item) { if (_hashSet.Remove(item)) { // 重新构建队列,移除指定项 var tempQueue = new Queue(); while (_queue.Count > 0) { var current = _queue.Dequeue(); if (!_comparer.Equals(current, item)) { tempQueue.Enqueue(current); } } // 将临时队列的内容复制回原队列 while (tempQueue.Count > 0) { _queue.Enqueue(tempQueue.Dequeue()); } return true; } return false; } /// /// 将 UniqueQueue 复制到新数组 /// /// 包含 UniqueQueue 元素副本的新数组 public T[] ToArray() { return _queue.ToArray(); } /// /// 将 UniqueQueue 元素复制到现有一维数组中 /// /// 一维数组,它是从 UniqueQueue 复制的元素的目标 /// array 中从零开始的索引,从此处开始复制 public void CopyTo(T[] array, int arrayIndex) { _queue.CopyTo(array, arrayIndex); } /// /// 设置容量为 UniqueQueue 中元素的实际数目 /// public void TrimExcess() { _queue.TrimExcess(); _hashSet.TrimExcess(); } #endregion 集合操作 #region 接口实现 /// /// 返回循环访问 UniqueQueue 的枚举数 /// /// UniqueQueue 的枚举数 public IEnumerator GetEnumerator() { return _queue.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => ((ICollection)_queue).SyncRoot; void ICollection.CopyTo(Array array, int index) { ((ICollection)_queue).CopyTo(array, index); } #endregion 接口实现 #region 性能优化方法 /// /// 批量入队多个元素 /// /// 要入队的元素集合 /// 成功添加的元素数量 public int EnqueueRange(IEnumerable items) { if (items == null) throw new ArgumentNullException(nameof(items)); return items.Count(Enqueue); } /// /// 批量出队多个元素 /// /// 要出队的元素数量 /// 出队的元素集合 public IEnumerable DequeueRange(int count) { if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "数量不能为负数。"); var result = new List(Math.Min(count, _queue.Count)); for (int i = 0; i < count && _queue.Count > 0; i++) { result.Add(Dequeue()); } return result; } #endregion 性能优化方法 } // 扩展方法类,提供额外的便利方法 public static class HashQueueExtensions { /// /// 将元素入队,如果队列已满则移除最旧的元素 /// /// 元素类型 /// UniqueQueue 实例 /// 要入队的元素 /// 队列的最大大小 /// 如果移除了旧元素返回 true,否则返回 false public static bool EnqueueWithLimit(this HashQueue queue, T item, int maxSize) { if (queue == null) throw new ArgumentNullException(nameof(queue)); if (maxSize <= 0) throw new ArgumentOutOfRangeException(nameof(maxSize), "最大大小必须大于0。"); var removedOld = false; if (queue.Count >= maxSize && !queue.Contains(item)) { queue.Dequeue(); removedOld = true; } queue.Enqueue(item); return removedOld; } /// /// 将元素入队,如果队列已满则移除最旧的元素 /// /// 元素类型 /// UniqueQueue 实例 /// 要入队的元素 /// 队列的最大大小 /// 如果移除了旧元素返回 true,否则返回 false public static bool EnqueueWithLimit(this ConcurrentHashQueue queue, T item, int maxSize) { if (queue == null) throw new ArgumentNullException(nameof(queue)); if (maxSize <= 0) throw new ArgumentOutOfRangeException(nameof(maxSize), "最大大小必须大于0。"); var removedOld = false; if (queue.Count >= maxSize && !queue.Contains(item)) { removedOld = queue.TryDequeue(out _); } queue.Enqueue(item); return removedOld; } /// /// 批量出队所有元素 /// /// 元素类型 /// ConcurrentUniqueQueue 实例 /// 所有出队的元素 public static IEnumerable DequeueAll(this HashQueue queue) { if (queue == null) throw new ArgumentNullException(nameof(queue)); return queue.DequeueRange(queue.Count); } /// /// 批量出队所有元素 /// /// 元素类型 /// ConcurrentUniqueQueue 实例 /// 所有出队的元素 public static IEnumerable DequeueAll(this ConcurrentHashQueue queue) { if (queue == null) throw new ArgumentNullException(nameof(queue)); return queue.DequeueRange(queue.Count); } /// /// 安全地枚举队列元素(快照) /// /// 元素类型 /// ConcurrentUniqueQueue 实例 /// 队列元素的快照 public static IReadOnlyList ToSnapshot(this HashQueue queue) { if (queue == null) throw new ArgumentNullException(nameof(queue)); return queue.ToArray(); } /// /// 安全地枚举队列元素(快照) /// /// 元素类型 /// ConcurrentUniqueQueue 实例 /// 队列元素的快照 public static IReadOnlyList ToSnapshot(this ConcurrentHashQueue queue) { if (queue == null) throw new ArgumentNullException(nameof(queue)); return queue.ToArray(); } }