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();
}
}