Revert "提交修改"

This reverts commit 5750c4fe56.
This commit is contained in:
2025-10-29 22:41:47 +08:00
parent 5750c4fe56
commit 234b18d3f8
2148 changed files with 5550 additions and 13963 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0b3d477f03f404310b5ad48f89d1f193
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,346 @@
using System;
using System.Collections.Generic;
using System.IO;
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace NBC.DataStructure.Collection
{
/// 环形缓存自增式缓存自动扩充、不会收缩缓存、所以不要用这个操作过大的IO流
/// 1、环大小8192溢出的会自动增加环的大小。
/// 2、每个块都是一个环形缓存当溢出的时候会自动添加到下一个环中。
/// 3、当读取完成后用过的环会放在缓存中不会销毁掉。
/// <summary>
/// 自增式缓存类,继承自 Stream 和 IDisposable 接口。
/// 环形缓存具有自动扩充的特性,但不会收缩,适用于操作不过大的 IO 流。
/// </summary>
public sealed class CircularBuffer : Stream, IDisposable
{
private byte[] _lastBuffer;
/// <summary>
/// 环形缓存块的默认大小
/// </summary>
public const int ChunkSize = 8192;
private readonly Queue<byte[]> _bufferCache = new Queue<byte[]>();
private readonly Queue<byte[]> _bufferQueue = new Queue<byte[]>();
/// <summary>
/// 获取或设置环形缓存的第一个索引位置
/// </summary>
public int FirstIndex { get; set; }
/// <summary>
/// 获取或设置环形缓存的最后一个索引位置
/// </summary>
public int LastIndex { get; set; }
/// <summary>
/// 获取环形缓存的总长度
/// </summary>
public override long Length
{
get
{
if (_bufferQueue.Count == 0)
{
return 0;
}
return (_bufferQueue.Count - 1) * ChunkSize + LastIndex - FirstIndex;
}
}
/// <summary>
/// 获取环形缓存的第一个块
/// </summary>
public byte[] First
{
get
{
if (_bufferQueue.Count == 0)
{
AddLast();
}
return _bufferQueue.Peek();
}
}
/// <summary>
/// 获取环形缓存的最后一个块
/// </summary>
public byte[] Last
{
get
{
if (_bufferQueue.Count == 0)
{
AddLast();
}
return _lastBuffer;
}
}
/// <summary>
/// 向环形缓存中添加一个新的块
/// </summary>
public void AddLast()
{
var buffer = _bufferCache.Count > 0 ? _bufferCache.Dequeue() : new byte[ChunkSize];
_bufferQueue.Enqueue(buffer);
_lastBuffer = buffer;
}
/// <summary>
/// 从环形缓存中移除第一个块
/// </summary>
public void RemoveFirst()
{
_bufferCache.Enqueue(_bufferQueue.Dequeue());
}
/// <summary>
/// 从流中读取指定数量的数据到缓存。
/// </summary>
/// <param name="stream">源数据流。</param>
/// <param name="count">要读取的字节数。</param>
public void Read(Stream stream, int count)
{
if (count > Length)
{
throw new Exception($"bufferList length < count, {Length} {count}");
}
var copyCount = 0;
while (copyCount < count)
{
var n = count - copyCount;
if (ChunkSize - FirstIndex > n)
{
stream.Write(First, FirstIndex, n);
FirstIndex += n;
copyCount += n;
}
else
{
stream.Write(First, FirstIndex, ChunkSize - FirstIndex);
copyCount += ChunkSize - FirstIndex;
FirstIndex = 0;
RemoveFirst();
}
}
}
/// <summary>
/// 从缓存中读取指定数量的数据到内存。
/// </summary>
/// <param name="memory">目标内存。</param>
/// <param name="count">要读取的字节数。</param>
public void Read(Memory<byte> memory, int count)
{
if (count > Length)
{
throw new Exception($"bufferList length < count, {Length} {count}");
}
var copyCount = 0;
while (copyCount < count)
{
var n = count - copyCount;
var asMemory = First.AsMemory();
if (ChunkSize - FirstIndex > n)
{
var slice = asMemory.Slice(FirstIndex, n);
slice.CopyTo(memory.Slice(copyCount, n));
FirstIndex += n;
copyCount += n;
}
else
{
var length = ChunkSize - FirstIndex;
var slice = asMemory.Slice(FirstIndex, length);
slice.CopyTo(memory.Slice(copyCount, length));
copyCount += ChunkSize - FirstIndex;
FirstIndex = 0;
RemoveFirst();
}
}
}
/// <summary>
/// 从自定义流中读取数据到指定的缓冲区。
/// </summary>
/// <param name="buffer">目标缓冲区,用于存储读取的数据。</param>
/// <param name="offset">目标缓冲区中的起始偏移量。</param>
/// <param name="count">要读取的字节数。</param>
/// <returns>实际读取的字节数。</returns>
public override int Read(byte[] buffer, int offset, int count)
{
if (buffer.Length < offset + count)
{
throw new Exception($"buffer length < count, buffer length: {buffer.Length} {offset} {count}");
}
var length = Length;
if (length < count)
{
count = (int) length;
}
var copyCount = 0;
// 循环直到成功读取所需的字节数
while (copyCount < count)
{
var copyLength = count - copyCount;
if (ChunkSize - FirstIndex > copyLength)
{
// 将数据从当前块的缓冲区复制到目标缓冲区
Array.Copy(First, FirstIndex, buffer, copyCount + offset, copyLength);
FirstIndex += copyLength;
copyCount += copyLength;
continue;
}
// 复制当前块中剩余的数据,并切换到下一个块
Array.Copy(First, FirstIndex, buffer, copyCount + offset, ChunkSize - FirstIndex);
copyCount += ChunkSize - FirstIndex;
FirstIndex = 0;
RemoveFirst();
}
return count;
}
/// <summary>
/// 将数据从给定的字节数组写入流中。
/// </summary>
/// <param name="buffer">包含要写入的数据的字节数组。</param>
public void Write(byte[] buffer)
{
Write(buffer, 0, buffer.Length);
}
/// <summary>
/// 将数据从给定的流写入流中。
/// </summary>
/// <param name="stream">包含要写入的数据的流。</param>
public void Write(Stream stream)
{
var copyCount = 0;
var count = (int) (stream.Length - stream.Position);
while (copyCount < count)
{
if (LastIndex == ChunkSize)
{
AddLast();
LastIndex = 0;
}
var n = count - copyCount;
if (ChunkSize - LastIndex > n)
{
_ = stream.Read(Last, LastIndex, n);
LastIndex += count - copyCount;
copyCount += n;
}
else
{
_ = stream.Read(Last, LastIndex, ChunkSize - LastIndex);
copyCount += ChunkSize - LastIndex;
LastIndex = ChunkSize;
}
}
}
/// <summary>
/// 将数据从给定的字节数组写入流中。
/// </summary>
/// <param name="buffer">包含要写入的数据的字节数组。</param>
/// <param name="offset">开始写入的缓冲区中的索引。</param>
/// <param name="count">要写入的字节数。</param>
public override void Write(byte[] buffer, int offset, int count)
{
var copyCount = 0;
while (copyCount < count)
{
if (ChunkSize == LastIndex)
{
AddLast();
LastIndex = 0;
}
var byteLength = count - copyCount;
if (ChunkSize - LastIndex > byteLength)
{
Array.Copy(buffer, copyCount + offset, Last, LastIndex, byteLength);
LastIndex += byteLength;
copyCount += byteLength;
}
else
{
Array.Copy(buffer, copyCount + offset, Last, LastIndex, ChunkSize - LastIndex);
copyCount += ChunkSize - LastIndex;
LastIndex = ChunkSize;
}
}
}
/// <summary>
/// 获取一个值,指示流是否支持读取操作。
/// </summary>
public override bool CanRead { get; } = true;
/// <summary>
/// 获取一个值,指示流是否支持寻找操作。
/// </summary>
public override bool CanSeek { get; } = false;
/// <summary>
/// 获取一个值,指示流是否支持写入操作。
/// </summary>
public override bool CanWrite { get; } = true;
/// <summary>
/// 获取或设置流中的位置。
/// </summary>
public override long Position { get; set; }
/// <summary>
/// 刷新流(在此实现中引发未实现异常)。
/// </summary>
public override void Flush()
{
throw new NotImplementedException();
}
/// <summary>
/// 在流中寻找特定位置(在此实现中引发未实现异常)。
/// </summary>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
/// <summary>
/// 设置流的长度(在此实现中引发未实现异常)。
/// </summary>
public override void SetLength(long value)
{
throw new NotImplementedException();
}
/// <summary>
/// 释放 CustomStream 使用的所有资源。
/// </summary>
public new void Dispose()
{
_bufferQueue.Clear();
_lastBuffer = null;
FirstIndex = 0;
LastIndex = 0;
base.Dispose();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38264758f23d1448a975efc4f4b7da63
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,197 @@
#if !FANTASY_WEBGL
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using NBC.Pool;
#pragma warning disable CS8603 // Possible null reference return.
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 并发的一对多列表池,用于维护具有相同键的多个值的关联关系,实现了 <see cref="IDisposable"/> 接口。
/// </summary>
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class ConcurrentOneToManyListPool<TKey, TValue> : ConcurrentOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="ConcurrentOneToManyListPool{TKey, TValue}"/> 的实例。
/// </summary>
/// <returns>创建的实例。</returns>
public static ConcurrentOneToManyListPool<TKey, TValue> Create()
{
var a = MultiThreadPool.Rent<ConcurrentOneToManyListPool<TKey, TValue>>();
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放实例占用的资源。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
// 清空实例的数据
Clear();
// 将实例返回到池中以便重用
MultiThreadPool.Return(this);
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 并发的一对多列表,用于维护具有相同键的多个值的关联关系。
/// </summary>
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class ConcurrentOneToManyList<TKey, TValue> : ConcurrentDictionary<TKey, List<TValue>> where TKey : notnull
{
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
private readonly int _recyclingLimit = 120;
/// <summary>
/// 初始化 <see cref="ConcurrentOneToManyList{TKey, TValue}"/> 类的新实例。
/// </summary>
public ConcurrentOneToManyList()
{
}
/// <summary>
/// 设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public ConcurrentOneToManyList(int recyclingLimit)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 判断指定键的列表是否包含指定值。
/// </summary>
/// <param name="key">要搜索的键。</param>
/// <param name="value">要搜索的值。</param>
/// <returns>如果列表包含值,则为 true否则为 false。</returns>
public bool Contains(TKey key, TValue value)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
/// <summary>
/// 向指定键的列表中添加一个值。
/// </summary>
/// <param name="key">要添加值的键。</param>
/// <param name="value">要添加的值。</param>
public void Add(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
list = Fetch();
list.Add(value);
base[key] = list;
return;
}
list.Add(value);
}
/// <summary>
/// 获取指定键的列表中的第一个值。
/// </summary>
/// <param name="key">要获取第一个值的键。</param>
/// <returns>指定键的列表中的第一个值,如果不存在则为默认值。</returns>
public TValue First(TKey key)
{
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
}
/// <summary>
/// 从指定键的列表中移除一个值。
/// </summary>
/// <param name="key">要移除值的键。</param>
/// <param name="value">要移除的值。</param>
public void RemoveValue(TKey key, TValue value)
{
if (!TryGetValue(key, out var list)) return;
list.Remove(value);
if (list.Count == 0) RemoveKey(key);
}
/// <summary>
/// 从字典中移除指定键以及其关联的列表。
/// </summary>
/// <param name="key">要移除的键。</param>
public void RemoveKey(TKey key)
{
if (!TryRemove(key, out var list)) return;
Recycle(list);
}
/// <summary>
/// 从队列中获取一个列表,如果队列为空则创建一个新的列表。
/// </summary>
/// <returns>获取的列表。</returns>
private List<TValue> Fetch()
{
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
}
/// <summary>
/// 将一个列表回收到队列中。
/// </summary>
/// <param name="list">要回收的列表。</param>
private void Recycle(List<TValue> list)
{
list.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
_queue.Enqueue(list);
}
/// <summary>
/// 清空当前类的数据,包括从基类继承的数据以及自定义的数据队列。
/// </summary>
protected new void Clear()
{
base.Clear();
_queue.Clear();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 21b2d3135b133441aa08c5eb1768a103
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,194 @@
#if !FANTASY_WEBGL
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using NBC.Pool;
#pragma warning disable CS8603
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 表示一个并发的一对多队列池,用于维护具有相同键的多个值的关联关系,实现了 <see cref="IDisposable"/> 接口。
/// </summary>
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class ConcurrentOneToManyQueuePool<TKey, TValue> : ConcurrentOneToManyQueue<TKey, TValue>, IDisposable, IPool where TKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建并返回一个 <see cref="ConcurrentOneToManyQueuePool{TKey, TValue}"/> 的实例。
/// </summary>
/// <returns>创建的实例。</returns>
public static ConcurrentOneToManyQueuePool<TKey, TValue> Create()
{
var a = MultiThreadPool.Rent<ConcurrentOneToManyQueuePool<TKey, TValue>>();
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放当前实例所占用的资源,并将实例返回到对象池中,以便重用。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
// 将实例返回到对象池中,以便重用
MultiThreadPool.Return(this);
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 表示一个并发的一对多队列,用于维护具有相同键的多个值的关联关系。
/// </summary>
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class ConcurrentOneToManyQueue<TKey, TValue> : ConcurrentDictionary<TKey, Queue<TValue>> where TKey : notnull
{
private readonly Queue<Queue<TValue>> _queue = new Queue<Queue<TValue>>();
private readonly int _recyclingLimit;
/// <summary>
/// 设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public ConcurrentOneToManyQueue(int recyclingLimit = 0)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 判断指定键的队列是否包含指定值。
/// </summary>
/// <param name="key">要搜索的键。</param>
/// <param name="value">要搜索的值。</param>
/// <returns>如果队列包含值,则为 true否则为 false。</returns>
public bool Contains(TKey key, TValue value)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
/// <summary>
/// 向指定键的队列中添加一个值。
/// </summary>
/// <param name="key">要添加值的键。</param>
/// <param name="value">要添加的值。</param>
public void Enqueue(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
list = Fetch();
list.Enqueue(value);
TryAdd(key, list);
return;
}
list.Enqueue(value);
}
/// <summary>
/// 从指定键的队列中出队并返回一个值。
/// </summary>
/// <param name="key">要出队的键。</param>
/// <returns>出队的值,如果队列为空则为默认值。</returns>
public TValue Dequeue(TKey key)
{
if (!TryGetValue(key, out var list) || list.Count == 0) return default;
var value = list.Dequeue();
if (list.Count == 0) RemoveKey(key);
return value;
}
/// <summary>
/// 尝试从指定键的队列中出队一个值。
/// </summary>
/// <param name="key">要出队的键。</param>
/// <param name="value">出队的值,如果队列为空则为默认值。</param>
/// <returns>如果成功出队,则为 true否则为 false。</returns>
public bool TryDequeue(TKey key, out TValue value)
{
value = Dequeue(key);
return value != null;
}
/// <summary>
/// 从字典中移除指定键以及其关联的队列。
/// </summary>
/// <param name="key">要移除的键。</param>
public void RemoveKey(TKey key)
{
if (!TryGetValue(key, out var list)) return;
TryRemove(key, out _);
Recycle(list);
}
/// <summary>
/// 从队列中获取一个新的队列,如果队列为空则创建一个新的队列。
/// </summary>
/// <returns>获取的队列。</returns>
private Queue<TValue> Fetch()
{
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
}
/// <summary>
/// 将一个队列回收到队列池中。
/// </summary>
/// <param name="list">要回收的队列。</param>
private void Recycle(Queue<TValue> list)
{
list.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
_queue.Enqueue(list);
}
/// <summary>
/// 清空当前类的数据,包括从基类继承的键值对字典中的数据以及自定义的队列池。
/// </summary>
protected new void Clear()
{
base.Clear();
_queue.Clear();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50a0f004d8b7b4075a4ce01376e49375
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 可释放的哈希集合对象池。
/// </summary>
/// <typeparam name="T">哈希集合中元素的类型。</typeparam>
public sealed class HashSetPool<T> : HashSet<T>, IDisposable, IPool
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<HashSetPool<T>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 创建一个 <see cref="HashSetPool{T}"/> 哈希集合池的实例。
/// </summary>
/// <returns>创建的实例。</returns>
public static HashSetPool<T> Create()
{
#if FANTASY_WEBGL
var list = Pool<HashSetPool<T>>.Rent();
list._isDispose = false;
list._isPool = true;
return list;
#else
var list = MultiThreadPool.Rent<HashSetPool<T>>();
list._isDispose = false;
list._isPool = true;
return list;
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 基本哈希集合对象池,他自持有实际的哈希集合。
/// </summary>
/// <typeparam name="T">哈希集合中元素的类型。</typeparam>
public sealed class HashSetBasePool<T> : IDisposable, IPool
{
private bool _isPool;
/// <summary>
/// 存储实际的哈希集合
/// </summary>
public HashSet<T> Set = new HashSet<T>();
/// <summary>
/// 创建一个 <see cref="HashSetBasePool{T}"/> 基本哈希集合对象池的实例。
/// </summary>
/// <returns>创建的实例。</returns>
public static HashSetBasePool<T> Create()
{
#if FANTASY_WEBGL
var hashSetBasePool = Pool<HashSetBasePool<T>>.Rent();
hashSetBasePool._isPool = true;
return hashSetBasePool;
#else
var hashSetBasePool = MultiThreadPool.Rent<HashSetBasePool<T>>();
hashSetBasePool._isPool = true;
return hashSetBasePool;
#endif
}
/// <summary>
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
/// </summary>
public void Dispose()
{
Set.Clear();
#if FANTASY_WEBGL
Pool<HashSetBasePool<T>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c49468bddc6524c419ace92b9ad68a5d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 可释放的列表List对象池。
/// </summary>
/// <typeparam name="T">列表中元素的类型。</typeparam>
public sealed class ListPool<T> : List<T>, IDisposable, IPool
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<ListPool<T>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 使用指定的元素创建一个 <see cref="ListPool{T}"/> 列表List对象池的实例。
/// </summary>
/// <param name="args">要添加到列表的元素。</param>
/// <returns>创建的实例。</returns>
public static ListPool<T> Create(params T[] args)
{
#if FANTASY_WEBGL
var list = Pool<ListPool<T>>.Rent();
#else
var list = MultiThreadPool.Rent<ListPool<T>>();
#endif
list._isDispose = false;
list._isPool = true;
if (args != null)
{
list.AddRange(args);
}
return list;
}
/// <summary>
/// 使用指定的列表创建一个 <see cref="ListPool{T}"/> 列表List对象池的实例。
/// </summary>
/// <param name="args">要添加到列表的元素列表。</param>
/// <returns>创建的实例。</returns>
public static ListPool<T> Create(List<T> args)
{
#if FANTASY_WEBGL
var list = Pool<ListPool<T>>.Rent();
#else
var list = MultiThreadPool.Rent<ListPool<T>>();
#endif
list._isDispose = false;
list._isPool = true;
if (args != null)
{
list.AddRange(args);
}
return list;
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f629886e3686046c6b00225679a7dbf5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 一对多哈希集合OneToManyHashSet对象池。
/// </summary>
/// <typeparam name="TKey">键的类型。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class OneToManyHashSetPool<TKey, TValue> : OneToManyHashSet<TKey, TValue>, IDisposable, IPool where TKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="OneToManyHashSetPool{TKey, TValue}"/> 一对多哈希集合OneToManyHashSet对象池的实例。
/// </summary>
/// <returns>创建的实例。</returns>
public static OneToManyHashSetPool<TKey, TValue> Create()
{
#if FANTASY_WEBGL
var a = Pool<OneToManyHashSetPool<TKey, TValue>>.Rent();
#else
var a = MultiThreadPool.Rent<OneToManyHashSetPool<TKey, TValue>>();
#endif
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<OneToManyHashSetPool<TKey, TValue>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 一对多哈希集合OneToManyHashSet用于创建和管理键对应多个值的集合。
/// </summary>
/// <typeparam name="TKey">键的类型。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class OneToManyHashSet<TKey, TValue> : Dictionary<TKey, HashSet<TValue>> where TKey : notnull
{
/// 用于回收和重用的空闲值集合队列。
private readonly Queue<HashSet<TValue>> _queue = new Queue<HashSet<TValue>>();
/// 设置最大回收限制,用于控制值集合的最大数量。
private readonly int _recyclingLimit = 120;
/// 一个空的、不包含任何元素的哈希集合,用于在查找失败时返回。
private static HashSet<TValue> _empty = new HashSet<TValue>();
/// <summary>
/// 初始化 <see cref="OneToManyHashSet{TKey, TValue}"/> 类的新实例。
/// </summary>
public OneToManyHashSet() { }
/// <summary>
/// 设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public OneToManyHashSet(int recyclingLimit)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 判断指定的键值对是否存在于集合中。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
/// <returns>如果存在则为 true否则为 false。</returns>
public bool Contains(TKey key, TValue value)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
/// <summary>
/// 添加指定的键值对到集合中。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
public void Add(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
list = Fetch();
list.Add(value);
Add(key, list);
return;
}
list.Add(value);
}
/// <summary>
/// 从集合中移除指定键对应的值。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">要移除的值。</param>
public void RemoveValue(TKey key, TValue value)
{
if (!TryGetValue(key, out var list)) return;
list.Remove(value);
if (list.Count == 0) RemoveKey(key);
}
/// <summary>
/// 从集合中移除指定键及其对应的值集合。
/// </summary>
/// <param name="key">键。</param>
public void RemoveKey(TKey key)
{
if (!TryGetValue(key, out var list)) return;
Remove(key);
Recycle(list);
}
/// <summary>
/// 获取指定键对应的值集合,如果不存在则返回一个空的哈希集合。
/// </summary>
/// <param name="key">键。</param>
/// <returns>对应的值集合或空的哈希集合。</returns>
public HashSet<TValue> GetValue(TKey key)
{
if (TryGetValue(key, out HashSet<TValue> value))
{
return value;
}
return _empty;
}
/// <summary>
/// 从队列中获取一个空闲的值集合,或者创建一个新的。
/// </summary>
/// <returns>值集合。</returns>
private HashSet<TValue> Fetch()
{
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
}
/// <summary>
/// 回收值集合到队列中,以便重复利用。
/// </summary>
/// <param name="list">要回收的值集合。</param>
private void Recycle(HashSet<TValue> list)
{
list.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
_queue.Enqueue(list);
}
/// <summary>
/// 清空集合中的数据并和队列。
/// </summary>
protected new void Clear()
{
base.Clear();
_queue.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e0cac58c09d6429faba279d03ef2317
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NBC.Pool;
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8603 // Possible null reference return.
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 可回收的、一对多关系的列表池。
/// </summary>
/// <typeparam name="TKey">键的类型。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class OneToManyListPool<TKey, TValue> : OneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="OneToManyListPool{TKey, TValue}"/> 一对多关系的列表池的实例。
/// </summary>
/// <returns>创建的实例。</returns>
public static OneToManyListPool<TKey, TValue> Create()
{
#if FANTASY_WEBGL || FANTASY_EXPORTER
var list = Pool<OneToManyListPool<TKey, TValue>>.Rent();
#else
var list = MultiThreadPool.Rent<OneToManyListPool<TKey, TValue>>();
#endif
list._isDispose = false;
list._isPool = true;
return list;
}
/// <summary>
/// 释放当前对象所占用的资源,并将对象回收到对象池中。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL || FANTASY_EXPORTER
Pool<OneToManyListPool<TKey, TValue>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 一对多关系的列表字典。
/// </summary>
/// <typeparam name="TKey">键的类型。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class OneToManyList<TKey, TValue> : Dictionary<TKey, List<TValue>> where TKey : notnull
{
private readonly int _recyclingLimit = 120;
private static readonly List<TValue> Empty = new List<TValue>();
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
/// <summary>
/// 初始化一个新的 <see cref="OneToManyList{TKey, TValue}"/> 实例。
/// </summary>
public OneToManyList() { }
/// <summary>
/// 对整个字典的所有 List 进行排序
/// </summary>
public void SortAll()
{
foreach (var key in Keys.ToList()) // 使用 ToList() 避免修改集合异常
{
this[key] = this[key].OrderByDescending(GetSortValue).ToList();
}
}
// 获取对象的排序值SortAttribute 的值,默认 0
private int GetSortValue(TValue obj)
{
if (obj == null) return 0;
var sortAttr = obj.GetType().GetCustomAttribute<SortAttribute>();
return sortAttr?.Sort ?? 0;
}
/// <summary>
/// 设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public OneToManyList(int recyclingLimit)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 判断给定的键和值是否存在于列表中。
/// </summary>
/// <param name="key">要搜索的键。</param>
/// <param name="value">要搜索的值。</param>
/// <returns>如果存在则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
public bool Contains(TKey key, TValue value)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
/// <summary>
/// 向列表中添加指定键和值。
/// </summary>
/// <param name="key">要添加值的键。</param>
/// <param name="value">要添加的值。</param>
public void Add(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
list = Fetch();
list.Add(value);
Add(key, list);
return;
}
list.Add(value);
}
/// <summary>
/// 获取指定键对应的列表中的第一个值。
/// </summary>
/// <param name="key">要获取值的键。</param>
/// <returns>键对应的列表中的第一个值。</returns>
public TValue First(TKey key)
{
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
}
/// <summary>
/// 从列表中移除指定键和值。
/// </summary>
/// <param name="key">要移除值的键。</param>
/// <param name="value">要移除的值。</param>
/// <returns>如果成功移除则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
public bool RemoveValue(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
return true;
}
var isRemove = list.Remove(value);
if (list.Count == 0)
{
isRemove = RemoveByKey(key);
}
return isRemove;
}
/// <summary>
/// 从列表中移除指定键及其关联的所有值。
/// </summary>
/// <param name="key">要移除的键。</param>
/// <returns>如果成功移除则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
public bool RemoveByKey(TKey key)
{
if (!TryGetValue(key, out var list))
{
return false;
}
Remove(key);
Recycle(list);
return true;
}
/// <summary>
/// 获取指定键关联的所有值的列表。
/// </summary>
/// <param name="key">要获取值的键。</param>
/// <returns>键关联的所有值的列表。</returns>
public List<TValue> GetValues(TKey key)
{
if (TryGetValue(key, out List<TValue> list))
{
return list;
}
return Empty;
}
/// <summary>
/// 清除字典中的所有键值对,并回收相关的值集合。
/// </summary>
public new void Clear()
{
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
base.Clear();
}
/// <summary>
/// 从空闲值集合队列中获取一个值集合,如果队列为空则创建一个新的值集合。
/// </summary>
/// <returns>从队列中获取的值集合。</returns>
private List<TValue> Fetch()
{
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
}
/// <summary>
/// 回收一个不再使用的值集合到空闲值集合队列中。
/// </summary>
/// <param name="list">要回收的值集合。</param>
private void Recycle(List<TValue> list)
{
list.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
_queue.Enqueue(list);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c48383c842aa40b683f03b79f845932
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
#pragma warning disable CS8603
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 支持一对多关系的队列池,用于存储具有相同键的值的队列集合。
/// </summary>
/// <typeparam name="TKey">键的类型。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class OneToManyQueuePool<TKey, TValue> : OneToManyQueue<TKey, TValue>, IDisposable, IPool where TKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="OneToManyQueuePool{TKey, TValue}"/> 一对多关系的队列池的实例。
/// </summary>
/// <returns>创建的实例。</returns>
public static OneToManyQueuePool<TKey, TValue> Create()
{
#if FANTASY_WEBGL
var a = Pool<OneToManyQueuePool<TKey, TValue>>.Rent();
#else
var a = MultiThreadPool.Rent<OneToManyQueuePool<TKey, TValue>>();
#endif
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放当前实例所占用的资源,并将实例回收到对象池中。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<OneToManyQueuePool<TKey, TValue>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 支持一对多关系的队列,用于存储具有相同键的值的队列集合。
/// </summary>
/// <typeparam name="TKey">键的类型。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class OneToManyQueue<TKey, TValue> : Dictionary<TKey, Queue<TValue>> where TKey : notnull
{
private readonly Queue<Queue<TValue>> _queue = new Queue<Queue<TValue>>();
private readonly int _recyclingLimit;
/// <summary>
/// 创建一个 <see cref="OneToManyQueue{TKey, TValue}"/> 一对多关系的队列的实例。设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public OneToManyQueue(int recyclingLimit = 0)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 判断指定键的值队列是否包含指定的值。
/// </summary>
/// <param name="key">要查找的键。</param>
/// <param name="value">要查找的值。</param>
/// <returns>如果存在,则为 <c>true</c>;否则为 <c>false</c>。</returns>
public bool Contains(TKey key, TValue value)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
/// <summary>
/// 将指定的值添加到指定键的值队列中。
/// </summary>
/// <param name="key">要添加值的键。</param>
/// <param name="value">要添加的值。</param>
public void Enqueue(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
list = Fetch();
list.Enqueue(value);
Add(key, list);
return;
}
list.Enqueue(value);
}
/// <summary>
/// 从指定键的值队列中出队一个值。
/// </summary>
/// <param name="key">要出队的键。</param>
/// <returns>出队的值。</returns>
public TValue Dequeue(TKey key)
{
if (!TryGetValue(key, out var list) || list.Count == 0)
{
return default;
}
var value = list.Dequeue();
if (list.Count == 0)
{
RemoveKey(key);
}
return value;
}
/// <summary>
/// 尝试从指定键的值队列中出队一个值。
/// </summary>
/// <param name="key">要出队的键。</param>
/// <param name="value">出队的值。</param>
/// <returns>如果成功出队,则为 <c>true</c>;否则为 <c>false</c>。</returns>
public bool TryDequeue(TKey key, out TValue value)
{
value = Dequeue(key);
return value != null;
}
/// <summary>
/// 从字典中移除指定键及其对应的值队列。
/// </summary>
/// <param name="key">要移除的键。</param>
public void RemoveKey(TKey key)
{
if (!TryGetValue(key, out var list)) return;
Remove(key);
Recycle(list);
}
/// <summary>
/// 从队列池中获取一个值队列。如果队列池为空,则创建一个新的值队列。
/// </summary>
/// <returns>获取的值队列。</returns>
private Queue<TValue> Fetch()
{
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
}
/// <summary>
/// 回收一个不再使用的值队列到队列池中,以便重用。
/// </summary>
/// <param name="list">要回收的值队列。</param>
private void Recycle(Queue<TValue> list)
{
list.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
_queue.Enqueue(list);
}
/// <summary>
/// 清空当前实例的数据,同时回收所有值队列。
/// </summary>
protected new void Clear()
{
base.Clear();
_queue.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8b6455b3359394987af4c0a4855189e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 可重用的列表,继承自 <see cref="List{T}"/> 类。该类支持通过对象池重用列表实例,以减少对象分配和释放的开销。
/// </summary>
/// <typeparam name="T">列表中元素的类型。</typeparam>
public sealed class ReuseList<T> : List<T>, IDisposable, IPool
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="ReuseList{T}"/> 可重用的列表的实例。
/// </summary>
/// <returns>创建的实例。</returns>
public static ReuseList<T> Create()
{
#if FANTASY_WEBGL
var list = Pool<ReuseList<T>>.Rent();
#else
var list = MultiThreadPool.Rent<ReuseList<T>>();
#endif
list._isDispose = false;
list._isPool = true;
return list;
}
/// <summary>
/// 释放该实例所占用的资源,并将实例返回到对象池中,以便重用。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<ReuseList<T>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9826877496b99479892a7359519298d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,226 @@
#if !FANTASY_WEBGL
using System;
using System.Collections.Generic;
using System.Linq;
using NBC.Pool;
#pragma warning disable CS8603
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 基于排序字典和并发集合实现的一对多映射列表的对象池包装类,继承自 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类,
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
/// </summary>
/// <typeparam name="TKey">键的类型。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class SortedConcurrentOneToManyListPool<TKey, TValue> : SortedConcurrentOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个新的 <see cref="SortedConcurrentOneToManyListPool{TKey, TValue}"/> 实例,使用默认的参数设置。
/// </summary>
/// <returns>新创建的 <see cref="SortedConcurrentOneToManyListPool{TKey, TValue}"/> 实例。</returns>
public static SortedConcurrentOneToManyListPool<TKey, TValue> Create()
{
var a = MultiThreadPool.Rent<SortedConcurrentOneToManyListPool<TKey, TValue>>();
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放当前对象池实例,将其返回到对象池以供重用。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
MultiThreadPool.Return(this);
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 基于排序字典和并发集合实现的一多对映射列表类,继承自 <see cref="SortedDictionary{TKey, TValue}"/> 类,
/// 用于在多个值与一个键关联的情况下进行管理和存储。该类支持并发操作,适用于多线程环境。
/// </summary>
/// <typeparam name="TKey">键的类型。</typeparam>
/// <typeparam name="TValue">值的类型。</typeparam>
public class SortedConcurrentOneToManyList<TKey, TValue> : SortedDictionary<TKey, List<TValue>> where TKey : notnull
{
/// 用于同步操作的锁对象,它确保在多线程环境下对数据的安全访问。
private readonly object _lockObject = new object();
/// 用于存储缓存的队列。
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
/// 控制缓存回收的限制。当缓存的数量超过此限制时,旧的缓存将会被回收。
private readonly int _recyclingLimit;
/// <summary>
/// 初始化一个新的 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类的实例,使用默认的参数设置。
/// </summary>
public SortedConcurrentOneToManyList()
{
}
/// <summary>
/// 初始化一个新的 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类的实例,指定最大缓存数量。
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public SortedConcurrentOneToManyList(int recyclingLimit = 0)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 检查指定的键和值是否存在于映射列表中。
/// </summary>
/// <param name="key">要检查的键。</param>
/// <param name="value">要检查的值。</param>
/// <returns>如果存在,则为 true否则为 false。</returns>
public bool Contains(TKey key, TValue value)
{
lock (_lockObject)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
}
/// <summary>
/// 将指定的值添加到与指定键关联的列表中。
/// </summary>
/// <param name="key">要关联值的键。</param>
/// <param name="value">要添加到列表的值。</param>
public void Add(TKey key, TValue value)
{
lock (_lockObject)
{
if (!TryGetValue(key, out var list))
{
list = Fetch();
list.Add(value);
base[key] = list;
return;
}
list.Add(value);
}
}
/// <summary>
/// 获取与指定键关联的列表中的第一个值。
/// 如果列表不存在或为空,则返回默认值。
/// </summary>
/// <param name="key">要获取第一个值的键。</param>
/// <returns>第一个值,或默认值。</returns>
public TValue First(TKey key)
{
lock (_lockObject)
{
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
}
}
/// <summary>
/// 从与指定键关联的列表中移除指定的值。
/// 如果列表不存在或值不存在于列表中,则不执行任何操作。
/// </summary>
/// <param name="key">要移除值的键。</param>
/// <param name="value">要移除的值。</param>
public void RemoveValue(TKey key, TValue value)
{
lock (_lockObject)
{
if (!TryGetValue(key, out var list)) return;
list.Remove(value);
if (list.Count == 0) RemoveKey(key);
}
}
/// <summary>
/// 从映射列表中移除指定的键及其关联的列表。
/// 如果键不存在于映射列表中,则不执行任何操作。
/// </summary>
/// <param name="key">要移除的键。</param>
public void RemoveKey(TKey key)
{
lock (_lockObject)
{
if (!TryGetValue(key, out var list)) return;
Remove(key);
Recycle(list);
}
}
/// <summary>
/// 从缓存中获取一个可重用的列表。如果缓存中不存在列表,则创建一个新的列表并返回。
/// </summary>
/// <returns>可重用的列表。</returns>
private List<TValue> Fetch()
{
lock (_lockObject)
{
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
}
}
/// <summary>
/// 将不再使用的列表回收到缓存中,以便重复利用。如果缓存数量超过限制,则丢弃列表而不进行回收。
/// </summary>
/// <param name="list">要回收的列表。</param>
private void Recycle(List<TValue> list)
{
lock (_lockObject)
{
list.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
_queue.Enqueue(list);
}
}
/// <summary>
/// 清空映射列表以及队列。
/// </summary>
protected new void Clear()
{
base.Clear();
_queue.Clear();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fef1d9dea81914fcf9fc9f5b1e5989d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 基于排序字典实现的一对多关系的映射哈希集合的对象池包装类,将唯一键映射到多个值的哈希集合。
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
/// </summary>
/// <typeparam name="TKey">字典中键的类型。</typeparam>
/// <typeparam name="TValue">哈希集合中值的类型。</typeparam>
public class SortedOneToManyHashSetPool<TKey, TValue> : SortedOneToManyHashSet<TKey, TValue>, IDisposable, IPool where TKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="SortedOneToManyHashSetPool{TKey, TValue}"/> 实例。
/// </summary>
/// <returns>新创建的实例。</returns>
public static SortedOneToManyHashSetPool<TKey, TValue> Create()
{
#if FANTASY_WEBGL
var a = Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Rent();
#else
var a = MultiThreadPool.Rent<SortedOneToManyHashSetPool<TKey, TValue>>();
#endif
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放当前对象池实例,将其返回到对象池以供重用。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 基于排序字典实现的一对多关系的映射哈希集合类,将唯一键映射到多个值的哈希集合。
/// 用于在多个值与一个键关联的情况下进行管理和存储。
/// </summary>
/// <typeparam name="TKey">字典中键的类型。</typeparam>
/// <typeparam name="TValue">集合中值的类型。</typeparam>
public class SortedOneToManyHashSet<TKey, TValue> : SortedDictionary<TKey, HashSet<TValue>> where TKey : notnull
{
private readonly Queue<HashSet<TValue>> _queue = new Queue<HashSet<TValue>>();
private readonly int _recyclingLimit = 120;
/// <summary>
/// 创建一个新的 <see cref="SortedOneToManyHashSet{TKey, TValue}"/> 实例。
/// </summary>
public SortedOneToManyHashSet() { }
/// <summary>
/// 创建一个新的 <see cref="SortedOneToManyHashSet{TKey, TValue}"/> 实例,设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public SortedOneToManyHashSet(int recyclingLimit)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 判断哈希集合中是否包含指定的键值对。
/// </summary>
/// <param name="key">要查找的键。</param>
/// <param name="value">要查找的值。</param>
/// <returns>如果键值对存在,则为 true否则为 false。</returns>
public bool Contains(TKey key, TValue value)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
/// <summary>
/// 将指定值添加到给定键关联的哈希集合中。
/// </summary>
/// <param name="key">要添加值的键。</param>
/// <param name="value">要添加的值。</param>
public void Add(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
list = Fetch();
list.Add(value);
Add(key, list);
return;
}
list.Add(value);
}
/// <summary>
/// 从指定键关联的哈希集合中移除特定值。
/// 如果哈希集合不存在或值不存在于集合中,则不执行任何操作。
/// </summary>
/// <param name="key">要移除值的键。</param>
/// <param name="value">要移除的值。</param>
public void RemoveValue(TKey key, TValue value)
{
if (!TryGetValue(key, out var list)) return;
list.Remove(value);
if (list.Count == 0) RemoveKey(key);
}
/// <summary>
/// 从字典中移除指定键以及关联的哈希集合,并将集合进行回收。
/// 如果键不存在于映射列表中,则不执行任何操作。
/// </summary>
/// <param name="key">要移除的键。</param>
public void RemoveKey(TKey key)
{
if (!TryGetValue(key, out var list)) return;
Remove(key);
Recycle(list);
}
/// <summary>
/// 获取一个空的或回收的哈希集合。
/// </summary>
/// <returns>获取的哈希集合实例。</returns>
private HashSet<TValue> Fetch()
{
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
}
/// <summary>
/// 回收一个哈希集合,将其清空并放入回收队列中。
/// </summary>
/// <param name="list">要回收的哈希集合。</param>
private void Recycle(HashSet<TValue> list)
{
list.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
_queue.Enqueue(list);
}
/// <summary>
/// 重写 Clear 方法,清空字典并清空回收队列。
/// </summary>
protected new void Clear()
{
base.Clear();
_queue.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4fe9032846c3f4a11be30459a9e12d7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,217 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NBC.Pool;
#pragma warning disable CS8603
namespace NBC.DataStructure.Collection
{
/// <summary>
/// 基于排序字典实现的一对多映射列表的对象池包装类,继承自 <see cref="SortedOneToManyList{TKey, TValue}"/> 类,
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
/// </summary>
/// <typeparam name="TKey">字典中键的类型。</typeparam>
/// <typeparam name="TValue">列表中值的类型。</typeparam>
public class SortedOneToManyListPool<TKey, TValue> : SortedOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="SortedOneToManyListPool{TKey, TValue}"/> 实例。
/// </summary>
/// <returns>新创建的实例。</returns>
public static SortedOneToManyListPool<TKey, TValue> Create()
{
#if FANTASY_WEBGL
var a = Pool<SortedOneToManyListPool<TKey, TValue>>.Rent();
#else
var a = MultiThreadPool.Rent<SortedOneToManyListPool<TKey, TValue>>();
#endif
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放当前对象池实例,将其返回到对象池以供重用。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<SortedOneToManyListPool<TKey, TValue>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 基于排序字典实现的一对多关系的映射列表类,将唯一键映射到包含多个值的列表。
/// 用于在多个值与一个键关联的情况下进行管理和存储。
/// </summary>
/// <typeparam name="TKey">字典中键的类型。</typeparam>
/// <typeparam name="TValue">列表中值的类型。</typeparam>
public class SortedOneToManyList<TKey, TValue> : SortedDictionary<TKey, List<TValue>> where TKey : notnull
{
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
private readonly int _recyclingLimit;
/// <summary>
/// 创建一个新的 <see cref="SortedOneToManyList{TKey, TValue}"/> 实例。
/// </summary>
public SortedOneToManyList()
{
}
/// <summary>
/// 创建一个新的 <see cref="SortedOneToManyList{TKey, TValue}"/> 实例,设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public SortedOneToManyList(int recyclingLimit = 0)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 判断列表中是否包含指定的键值对。
/// </summary>
/// <param name="key">要查找的键。</param>
/// <param name="value">要查找的值。</param>
/// <returns>如果键值对存在,则为 true否则为 false。</returns>
public bool Contains(TKey key, TValue value)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
/// <summary>
/// 将指定值添加到给定键关联的列表中。
/// </summary>
/// <param name="key">要添加值的键。</param>
/// <param name="value">要添加的值。</param>
public void Add(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
list = Fetch();
list.Add(value);
base[key] = list;
return;
}
list.Add(value);
}
/// <summary>
/// 获取指定键关联的列表中的第一个值。
/// </summary>
/// <param name="key">要查找值的键。</param>
/// <returns>指定键关联的列表中的第一个值,如果列表为空则返回默认值。</returns>
public TValue First(TKey key)
{
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
}
/// <summary>
/// 从指定键关联的列表中移除特定值。
/// </summary>
/// <param name="key">要移除值的键。</param>
/// <param name="value">要移除的值。</param>
public void RemoveValue(TKey key, TValue value)
{
if (!TryGetValue(key, out var list))
{
return;
}
list.Remove(value);
if (list.Count == 0)
{
RemoveKey(key);
}
}
/// <summary>
/// 从字典中移除指定键以及关联的列表,并将列表进行回收。
/// </summary>
/// <param name="key">要移除的键。</param>
public void RemoveKey(TKey key)
{
if (!TryGetValue(key, out var list))
{
return;
}
Remove(key);
Recycle(list);
}
/// <summary>
/// 获取一个空的或回收的列表。
/// </summary>
/// <returns>获取的列表实例。</returns>
private List<TValue> Fetch()
{
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
}
/// <summary>
/// 回收一个列表,将其清空并放入回收队列中。如果缓存数量超过限制,则丢弃列表而不进行回收
/// </summary>
/// <param name="list">要回收的列表。</param>
private void Recycle(List<TValue> list)
{
list.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
{
return;
}
_queue.Enqueue(list);
}
/// <summary>
/// 重写 Clear 方法,清空字典并清空回收队列。
/// </summary>
protected new void Clear()
{
base.Clear();
_queue.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e0621d52f951402daa68b108d7129f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4a444c902ff594bbaa3a3fb394e7f53a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
#pragma warning disable CS8601 // Possible null reference assignment.
namespace NBC.DataStructure.Dictionary
{
/// <summary>
/// 提供对字典的扩展方法。
/// </summary>
public static class DictionaryExtensions
{
/// <summary>
/// 尝试从字典中移除指定键,并返回相应的值。
/// </summary>
/// <typeparam name="T">字典中键的类型。</typeparam>
/// <typeparam name="TV">字典中值的类型。</typeparam>
/// <param name="self">要操作的字典实例。</param>
/// <param name="key">要移除的键。</param>
/// <param name="value">从字典中移除的值(如果成功移除)。</param>
/// <returns>如果成功移除键值对,则为 true否则为 false。</returns>
public static bool TryRemove<T, TV>(this IDictionary<T, TV> self, T key, out TV value)
{
if (!self.TryGetValue(key, out value))
{
return false;
}
self.Remove(key);
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6975fd47ec81d474cbd1ba996d01e864
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
namespace NBC.DataStructure.Dictionary
{
/// <summary>
/// 提供一个可以使用对象池管理的字典类。
/// </summary>
/// <typeparam name="TM">字典中键的类型。</typeparam>
/// <typeparam name="TN">字典中值的类型。</typeparam>
public sealed class DictionaryPool<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TM : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 释放实例占用的资源。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<DictionaryPool<TM, TN>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 创建一个新的 <see cref="DictionaryPool{TM, TN}"/> 实例。
/// </summary>
/// <returns>新创建的实例。</returns>
public static DictionaryPool<TM, TN> Create()
{
#if FANTASY_WEBGL
var dictionary = Pool<DictionaryPool<TM, TN>>.Rent();
#else
var dictionary = MultiThreadPool.Rent<DictionaryPool<TM, TN>>();
#endif
dictionary._isDispose = false;
dictionary._isPool = true;
return dictionary;
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 421f6528d2cff461dab994c2f358f24d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,289 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8603 // Possible null reference return.
namespace NBC.DataStructure.Dictionary
{
/// <summary>
/// 提供一个双向映射字典对象池类,用于双向键值对映射。
/// </summary>
/// <typeparam name="TKey">字典中键的类型。</typeparam>
/// <typeparam name="TValue">字典中值的类型。</typeparam>
public class DoubleMapDictionaryPool<TKey, TValue> : DoubleMapDictionary<TKey, TValue>, IDisposable, IPool where TKey : notnull where TValue : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个新的 <see cref="DoubleMapDictionaryPool{TKey, TValue}"/> 实例。
/// </summary>
/// <returns>新创建的实例。</returns>
public static DoubleMapDictionaryPool<TKey, TValue> Create()
{
#if FANTASY_WEBGL
var a = Pool<DoubleMapDictionaryPool<TKey, TValue>>.Rent();
#else
var a = MultiThreadPool.Rent<DoubleMapDictionaryPool<TKey, TValue>>();
#endif
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放实例占用的资源。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<DoubleMapDictionaryPool<TKey, TValue>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 可以实现双向映射的字典类,用于将键和值进行双向映射。
/// </summary>
/// <typeparam name="TK">键的类型,不能为 null。</typeparam>
/// <typeparam name="TV">值的类型,不能为 null。</typeparam>
public class DoubleMapDictionary<TK, TV> where TK : notnull where TV : notnull
{
private readonly Dictionary<TK, TV> _kv = new Dictionary<TK, TV>();
private readonly Dictionary<TV, TK> _vk = new Dictionary<TV, TK>();
/// <summary>
/// 创建一个新的空的 <see cref="DoubleMapDictionary{TK, TV}"/> 实例。
/// </summary>
public DoubleMapDictionary() { }
/// <summary>
/// 创建一个新的具有指定初始容量的 <see cref="DoubleMapDictionary{TK, TV}"/> 实例。
/// </summary>
/// <param name="capacity">初始容量。</param>
public DoubleMapDictionary(int capacity)
{
_kv = new Dictionary<TK, TV>(capacity);
_vk = new Dictionary<TV, TK>(capacity);
}
/// <summary>
/// 获取包含字典中所有键的列表。
/// </summary>
public List<TK> Keys => new List<TK>(_kv.Keys);
/// <summary>
/// 获取包含字典中所有值的列表。
/// </summary>
public List<TV> Values => new List<TV>(_vk.Keys);
/// <summary>
/// 对字典中的每个键值对执行指定的操作。
/// </summary>
/// <param name="action">要执行的操作。</param>
public void ForEach(Action<TK, TV> action)
{
if (action == null)
{
return;
}
var keys = _kv.Keys;
foreach (var key in keys)
{
action(key, _kv[key]);
}
}
/// <summary>
/// 将指定的键值对添加到字典中。
/// </summary>
/// <param name="key">要添加的键。</param>
/// <param name="value">要添加的值。</param>
public void Add(TK key, TV value)
{
if (key == null || value == null || _kv.ContainsKey(key) || _vk.ContainsKey(value))
{
return;
}
_kv.Add(key, value);
_vk.Add(value, key);
}
/// <summary>
/// 根据指定的键获取相应的值。
/// </summary>
/// <param name="key">要查找值的键。</param>
/// <returns>与指定键关联的值,如果找不到键,则返回默认值。</returns>
public TV GetValueByKey(TK key)
{
if (key != null && _kv.ContainsKey(key))
{
return _kv[key];
}
return default;
}
/// <summary>
/// 尝试根据指定的键获取相应的值。
/// </summary>
/// <param name="key">要查找值的键。</param>
/// <param name="value">如果找到,则为与指定键关联的值;否则为值的默认值。</param>
/// <returns>如果找到键,则为 true否则为 false。</returns>
public bool TryGetValueByKey(TK key, out TV value)
{
var result = key != null && _kv.ContainsKey(key);
value = result ? _kv[key] : default;
return result;
}
/// <summary>
/// 根据指定的值获取相应的键。
/// </summary>
/// <param name="value">要查找键的值。</param>
/// <returns>与指定值关联的键,如果找不到值,则返回默认键。</returns>
public TK GetKeyByValue(TV value)
{
if (value != null && _vk.ContainsKey(value))
{
return _vk[value];
}
return default;
}
/// <summary>
/// 尝试根据指定的值获取相应的键。
/// </summary>
/// <param name="value">要查找键的值。</param>
/// <param name="key">如果找到,则为与指定值关联的键;否则为键的默认值。</param>
/// <returns>如果找到值,则为 true否则为 false。</returns>
public bool TryGetKeyByValue(TV value, out TK key)
{
var result = value != null && _vk.ContainsKey(value);
key = result ? _vk[value] : default;
return result;
}
/// <summary>
/// 根据指定的键移除键值对。
/// </summary>
/// <param name="key">要移除的键。</param>
public void RemoveByKey(TK key)
{
if (key == null)
{
return;
}
if (!_kv.TryGetValue(key, out var value))
{
return;
}
_kv.Remove(key);
_vk.Remove(value);
}
/// <summary>
/// 根据指定的值移除键值对。
/// </summary>
/// <param name="value">要移除的值。</param>
public void RemoveByValue(TV value)
{
if (value == null)
{
return;
}
if (!_vk.TryGetValue(value, out var key))
{
return;
}
_kv.Remove(key);
_vk.Remove(value);
}
/// <summary>
/// 清空字典中的所有键值对。
/// </summary>
public void Clear()
{
_kv.Clear();
_vk.Clear();
}
/// <summary>
/// 判断字典是否包含指定的键。
/// </summary>
/// <param name="key">要检查的键。</param>
/// <returns>如果字典包含指定的键,则为 true否则为 false。</returns>
public bool ContainsKey(TK key)
{
return key != null && _kv.ContainsKey(key);
}
/// <summary>
/// 判断字典是否包含指定的值。
/// </summary>
/// <param name="value">要检查的值。</param>
/// <returns>如果字典包含指定的值,则为 true否则为 false。</returns>
public bool ContainsValue(TV value)
{
return value != null && _vk.ContainsKey(value);
}
/// <summary>
/// 判断字典是否包含指定的键值对。
/// </summary>
/// <param name="key">要检查的键。</param>
/// <param name="value">要检查的值。</param>
/// <returns>如果字典包含指定的键值对,则为 true否则为 false。</returns>
public bool Contains(TK key, TV value)
{
if (key == null || value == null)
{
return false;
}
return _kv.ContainsKey(key) && _vk.ContainsKey(value);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a23375ac49ac64827a5d2e6ae476bbab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
namespace NBC.DataStructure.Dictionary
{
/// <summary>
/// 提供一个带资源释放功能的实体字典类,支持使用对象池管理。
/// </summary>
/// <typeparam name="TM">字典中键的类型。</typeparam>
/// <typeparam name="TN">字典中值的类型,必须实现 IDisposable 接口。</typeparam>
public sealed class EntityDictionary<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TN : IDisposable where TM : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个新的 <see cref="EntityDictionary{TM, TN}"/> 实例。
/// </summary>
/// <returns>新创建的实例。</returns>
public static EntityDictionary<TM, TN> Create()
{
#if FANTASY_WEBGL
var entityDictionary = Pool<EntityDictionary<TM, TN>>.Rent();
#else
var entityDictionary = MultiThreadPool.Rent<EntityDictionary<TM, TN>>();
#endif
entityDictionary._isDispose = false;
entityDictionary._isPool = true;
return entityDictionary;
}
/// <summary>
/// 清空字典中的所有键值对,并释放值的资源。
/// </summary>
public new void Clear()
{
foreach (var keyValuePair in this)
{
keyValuePair.Value.Dispose();
}
base.Clear();
}
/// <summary>
/// 清空字典中的所有键值对,但不释放值的资源。
/// </summary>
public void ClearNotDispose()
{
base.Clear();
}
/// <summary>
/// 释放实例占用的资源。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<EntityDictionary<TM, TN>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 30708df4a385e4aee83551b3371240fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,247 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NBC.Pool;
#pragma warning disable CS8603
#pragma warning disable CS8601
namespace NBC.DataStructure.Dictionary
{
/// <summary>
/// 一对多映射关系的字典对象池。
/// </summary>
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
/// <typeparam name="TValueKey">内部字典中的键类型。</typeparam>
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
public class OneToManyDictionaryPool<TKey, TValueKey, TValue> : OneToManyDictionary<TKey, TValueKey, TValue>, IDisposable, IPool where TKey : notnull where TValueKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="OneToManyDictionaryPool{TKey, TValueKey, TValue}"/> 的实例。
/// </summary>
/// <returns>新创建的 OneToManyDictionaryPool 实例。</returns>
public static OneToManyDictionaryPool<TKey, TValueKey, TValue> Create()
{
#if FANTASY_WEBGL
var a = Pool<OneToManyDictionaryPool<TKey, TValueKey, TValue>>.Rent();
#else
var a = MultiThreadPool.Rent<OneToManyDictionaryPool<TKey, TValueKey, TValue>>();
#endif
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放当前实例及其资源。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<OneToManyDictionaryPool<TKey, TValueKey, TValue>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 一对多映射关系的字典。每个键都对应一个内部字典,该内部字典将键值映射到相应的值。
/// </summary>
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
/// <typeparam name="TValueKey">内部字典中的键类型。</typeparam>
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
public class OneToManyDictionary<TKey, TValueKey, TValue> : Dictionary<TKey, Dictionary<TValueKey, TValue>>
where TKey : notnull where TValueKey : notnull
{
private readonly Queue<Dictionary<TValueKey, TValue>> _queue = new Queue<Dictionary<TValueKey, TValue>>();
private readonly int _recyclingLimit = 120;
/// <summary>
/// 创建一个新的 <see cref="OneToManyDictionary{TKey, TValueKey, TValue}"/> 实例。
/// </summary>
public OneToManyDictionary() { }
/// <summary>
/// 创建一个新的 <see cref="OneToManyDictionary{TKey, TValueKey, TValue}"/> 实例,并指定最大缓存数量。
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public OneToManyDictionary(int recyclingLimit = 0)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 检查是否包含指定的键值对。
/// </summary>
/// <param name="key">外部字典中的键。</param>
/// <param name="valueKey">内部字典中的键。</param>
/// <returns>如果包含指定的键值对,则为 true否则为 false。</returns>
public bool Contains(TKey key, TValueKey valueKey)
{
TryGetValue(key, out var dic);
return dic != null && dic.ContainsKey(valueKey);
}
/// <summary>
/// 尝试获取指定键值对的值。
/// </summary>
/// <param name="key">外部字典中的键。</param>
/// <param name="valueKey">内部字典中的键。</param>
/// <param name="value">获取的值,如果操作成功,则为值;否则为默认值。</param>
/// <returns>如果操作成功,则为 true否则为 false。</returns>
public bool TryGetValue(TKey key, TValueKey valueKey, out TValue value)
{
value = default;
return TryGetValue(key, out var dic) && dic.TryGetValue(valueKey, out value);
}
/// <summary>
/// 获取指定键的第一个值。
/// </summary>
/// <param name="key">要获取第一个值的键。</param>
public TValue First(TKey key)
{
return !TryGetValue(key, out var dic) ? default : dic.First().Value;
}
/// <summary>
/// 向字典中添加指定的键值对。
/// </summary>
/// <param name="key">要添加键值对的键。</param>
/// <param name="valueKey">要添加键值对的内部字典键。</param>
/// <param name="value">要添加的值。</param>
public void Add(TKey key, TValueKey valueKey, TValue value)
{
if (!TryGetValue(key, out var dic))
{
dic = Fetch();
dic[valueKey] = value;
// dic.Add(valueKey, value);
Add(key, dic);
return;
}
dic[valueKey] = value;
// dic.Add(valueKey, value);
}
/// <summary>
/// 从字典中移除指定的键值对。
/// </summary>
/// <param name="key">要移除键值对的键。</param>
/// <param name="valueKey">要移除键值对的内部字典键。</param>
/// <returns>如果成功移除键值对,则为 true否则为 false。</returns>
public bool Remove(TKey key, TValueKey valueKey)
{
if (!TryGetValue(key, out var dic)) return false;
var result = dic.Remove(valueKey);
if (dic.Count == 0) RemoveKey(key);
return result;
}
/// <summary>
/// 从字典中移除指定的键值对。
/// </summary>
/// <param name="key">要移除键值对的键。</param>
/// <param name="valueKey">要移除键值对的内部字典键。</param>
/// <param name="value">如果成功移除键值对,则为移除的值;否则为默认值。</param>
/// <returns>如果成功移除键值对,则为 true否则为 false。</returns>
public bool Remove(TKey key, TValueKey valueKey, out TValue value)
{
if (!TryGetValue(key, out var dic))
{
value = default;
return false;
}
var result = dic.TryGetValue(valueKey, out value);
if (result) dic.Remove(valueKey);
if (dic.Count == 0) RemoveKey(key);
return result;
}
/// <summary>
/// 移除字典中的指定键及其相关的所有键值对。
/// </summary>
/// <param name="key">要移除的键。</param>
public void RemoveKey(TKey key)
{
if (!TryGetValue(key, out var dic)) return;
Remove(key);
Recycle(dic);
}
/// <summary>
/// 从对象池中获取一个内部字典实例,如果池中没有,则创建一个新实例。
/// </summary>
/// <returns>获取的内部字典实例。</returns>
private Dictionary<TValueKey, TValue> Fetch()
{
return _queue.Count <= 0 ? new Dictionary<TValueKey, TValue>() : _queue.Dequeue();
}
/// <summary>
/// 将不再使用的内部字典实例放回对象池中,以便后续重用。
/// </summary>
/// <param name="dic">要放回对象池的内部字典实例。</param>
private void Recycle(Dictionary<TValueKey, TValue> dic)
{
dic.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
_queue.Enqueue(dic);
}
/// <summary>
/// 清空字典中的所有键值对,并将不再使用的内部字典实例放回对象池中。
/// </summary>
public new void Clear()
{
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
base.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4cd83a6bbc1914aa4bf1f94f1cba1c47
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
#pragma warning disable CS8601
namespace NBC.DataStructure.Dictionary
{
/// <summary>
/// 一对多映射关系的排序字典对象池。
/// </summary>
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
/// <typeparam name="TSortedKey">内部字典中的排序键类型。</typeparam>
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
public class OneToManySortedDictionaryPool<TKey, TSortedKey, TValue> : OneToManySortedDictionary<TKey, TSortedKey, TValue>, IDisposable, IPool where TKey : notnull where TSortedKey : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个 <see cref="OneToManySortedDictionaryPool{TKey, TSortedKey, TValue}"/> 的实例。
/// </summary>
/// <returns>新创建的 OneToManySortedDictionaryPool 实例。</returns>
public static OneToManySortedDictionaryPool<TKey, TSortedKey, TValue> Create()
{
#if FANTASY_WEBGL
var a = Pool<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>.Rent();
#else
var a = MultiThreadPool.Rent<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>();
#endif
a._isDispose = false;
a._isPool = true;
return a;
}
/// <summary>
/// 释放当前实例及其资源。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 一对多映射关系的排序字典。每个外部键映射到一个内部排序字典,该内部排序字典将排序键映射到相应的值。
/// </summary>
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
/// <typeparam name="TSortedKey">内部字典中的排序键类型。</typeparam>
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
public class
OneToManySortedDictionary<TKey, TSortedKey, TValue> : Dictionary<TKey, SortedDictionary<TSortedKey, TValue>>
where TSortedKey : notnull where TKey : notnull
{
/// 缓存队列的回收限制
private readonly int _recyclingLimit = 120;
/// 缓存队列,用于存储已回收的内部排序字典
private readonly Queue<SortedDictionary<TSortedKey, TValue>> _queue = new Queue<SortedDictionary<TSortedKey, TValue>>();
/// <summary>
/// 创建一个新的 <see cref="OneToManySortedDictionary{TKey, TSortedKey, TValue}"/> 实例。
/// </summary>
protected OneToManySortedDictionary() { }
/// <summary>
/// 创建一个新的 <see cref="OneToManySortedDictionary{TKey, TSortedKey, TValue}"/> 实例。设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量全部缓存
/// </param>
public OneToManySortedDictionary(int recyclingLimit)
{
_recyclingLimit = recyclingLimit;
}
/// <summary>
/// 检查字典是否包含指定的外部键。
/// </summary>
/// <param name="key">要检查的外部键。</param>
/// <returns>如果字典包含指定的外部键,则为 true否则为 false。</returns>
public bool Contains(TKey key)
{
return this.ContainsKey(key);
}
/// <summary>
/// 检查字典是否包含指定的外部键和排序键。
/// </summary>
/// <param name="key">要检查的外部键。</param>
/// <param name="sortedKey">要检查的排序键。</param>
/// <returns>如果字典包含指定的外部键和排序键,则为 true否则为 false。</returns>
public bool Contains(TKey key, TSortedKey sortedKey)
{
return TryGetValue(key, out var dic) && dic.ContainsKey(sortedKey);
}
/// <summary>
/// 尝试从字典中获取指定外部键对应的内部排序字典。
/// </summary>
/// <param name="key">要获取内部排序字典的外部键。</param>
/// <param name="dic">获取到的内部排序字典,如果找不到则为 null。</param>
/// <returns>如果找到内部排序字典,则为 true否则为 false。</returns>
public new bool TryGetValue(TKey key, out SortedDictionary<TSortedKey, TValue> dic)
{
return base.TryGetValue(key, out dic);
}
/// <summary>
/// 尝试从字典中获取指定外部键和排序键对应的值。
/// </summary>
/// <param name="key">要获取值的外部键。</param>
/// <param name="sortedKey">要获取值的排序键。</param>
/// <param name="value">获取到的值,如果找不到则为 default。</param>
/// <returns>如果找到值,则为 true否则为 false。</returns>
public bool TryGetValueBySortedKey(TKey key, TSortedKey sortedKey, out TValue value)
{
if (base.TryGetValue(key, out var dic))
{
return dic.TryGetValue(sortedKey, out value);
}
value = default;
return false;
}
/// <summary>
/// 向字典中添加一个值,关联到指定的外部键和排序键。
/// </summary>
/// <param name="key">要关联值的外部键。</param>
/// <param name="sortedKey">要关联值的排序键。</param>
/// <param name="value">要添加的值。</param>
public void Add(TKey key, TSortedKey sortedKey, TValue value)
{
if (!TryGetValue(key, out var dic))
{
dic = Fetch();
dic.Add(sortedKey, value);
Add(key, dic);
return;
}
dic.Add(sortedKey, value);
}
/// <summary>
/// 从字典中移除指定外部键和排序键关联的值。
/// </summary>
/// <param name="key">要移除值的外部键。</param>
/// <param name="sortedKey">要移除值的排序键。</param>
/// <returns>如果成功移除值,则为 true否则为 false。</returns>
public bool RemoveSortedKey(TKey key, TSortedKey sortedKey)
{
if (!TryGetValue(key, out var dic))
{
return false;
}
var isRemove = dic.Remove(sortedKey);
if (dic.Count == 0)
{
isRemove = RemoveKey(key);
}
return isRemove;
}
/// <summary>
/// 从字典中移除指定外部键及其关联的所有值。
/// </summary>
/// <param name="key">要移除的外部键。</param>
/// <returns>如果成功移除外部键及其关联的所有值,则为 true否则为 false。</returns>
public bool RemoveKey(TKey key)
{
if (!TryGetValue(key, out var list))
{
return false;
}
Remove(key);
Recycle(list);
return true;
}
/// <summary>
/// 从缓存队列中获取一个内部排序字典。
/// </summary>
/// <returns>一个内部排序字典。</returns>
private SortedDictionary<TSortedKey, TValue> Fetch()
{
return _queue.Count <= 0 ? new SortedDictionary<TSortedKey, TValue>() : _queue.Dequeue();
}
/// <summary>
/// 回收一个内部排序字典到缓存队列。
/// </summary>
/// <param name="dic">要回收的内部排序字典。</param>
private void Recycle(SortedDictionary<TSortedKey, TValue> dic)
{
dic.Clear();
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
{
return;
}
_queue.Enqueue(dic);
}
/// <summary>
/// 清空字典以及内部排序字典缓存队列,释放所有资源。
/// </summary>
protected new void Clear()
{
base.Clear();
_queue.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ece24c9ac70d549ea953523c0e5f7337
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
namespace NBC.DataStructure.Dictionary
{
/// <summary>
/// 提供一个可以重用的字典类,支持使用对象池管理。
/// </summary>
/// <typeparam name="TM">字典中键的类型。</typeparam>
/// <typeparam name="TN">字典中值的类型。</typeparam>
public sealed class ReuseDictionary<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TM : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 创建一个新的 <see cref="ReuseDictionary{TM, TN}"/> 实例。
/// </summary>
/// <returns>新创建的实例。</returns>
public static ReuseDictionary<TM, TN> Create()
{
#if FANTASY_WEBGL
var entityDictionary = Pool<ReuseDictionary<TM, TN>>.Rent();
#else
var entityDictionary = MultiThreadPool.Rent<ReuseDictionary<TM, TN>>();
#endif
entityDictionary._isDispose = false;
entityDictionary._isPool = true;
return entityDictionary;
}
/// <summary>
/// 释放实例占用的资源。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<ReuseDictionary<TM, TN>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 882f28b6907a3465dbf9df04603f74b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using NBC.Pool;
namespace NBC.DataStructure.Dictionary
{
/// <summary>
/// 提供一个可以使用对象池管理的排序字典类。
/// </summary>
/// <typeparam name="TM"></typeparam>
/// <typeparam name="TN"></typeparam>
public sealed class SortedDictionaryPool<TM, TN> : SortedDictionary<TM, TN>, IDisposable, IPool where TM : notnull
{
private bool _isPool;
private bool _isDispose;
/// <summary>
/// 释放实例占用的资源。
/// </summary>
public void Dispose()
{
if (_isDispose)
{
return;
}
_isDispose = true;
Clear();
#if FANTASY_WEBGL
Pool<SortedDictionaryPool<TM, TN>>.Return(this);
#else
MultiThreadPool.Return(this);
#endif
}
/// <summary>
/// 创建一个新的 <see cref="SortedDictionaryPool{TM, TN}"/> 实例。
/// </summary>
/// <returns>新创建的实例。</returns>
public static SortedDictionaryPool<TM, TN> Create()
{
#if FANTASY_WEBGL
var dictionary = Pool<SortedDictionaryPool<TM, TN>>.Rent();
#else
var dictionary = MultiThreadPool.Rent<SortedDictionaryPool<TM, TN>>();
#endif
dictionary._isDispose = false;
dictionary._isPool = true;
return dictionary;
}
/// <summary>
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
public bool IsPool()
{
return _isPool;
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 060121583adb64f03973db7e623b91de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6e0c05d3a90bc4c5d88144a87507f06d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2af7e2648850348b591adabb5ba44532
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,121 @@
// ReSharper disable SwapViaDeconstruction
// ReSharper disable UseIndexFromEndExpression
// ReSharper disable ConvertToPrimaryConstructor
using System;
using System.Collections.Generic;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8601 // Possible null reference assignment.
namespace NBC.DataStructure.PriorityQueue
{
/// <summary>
/// 优先队列
/// </summary>
/// <typeparam name="TElement">节点数据</typeparam>
/// <typeparam name="TPriority">排序的类型、</typeparam>
public sealed class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TPriority>
{
private readonly List<PriorityQueueItem<TElement, TPriority>> _heap;
public PriorityQueue(int initialCapacity = 16)
{
_heap = new List<PriorityQueueItem<TElement, TPriority>>(initialCapacity);
}
public int Count => _heap.Count;
public void Enqueue(TElement element, TPriority priority)
{
_heap.Add(new PriorityQueueItem<TElement, TPriority>(element, priority));
HeapifyUp(_heap.Count - 1);
}
public TElement Dequeue()
{
if (_heap.Count == 0)
{
throw new InvalidOperationException("The queue is empty.");
}
var item = _heap[0];
_heap[0] = _heap[_heap.Count - 1];
_heap.RemoveAt(_heap.Count - 1);
HeapifyDown(0);
return item.Element;
}
public bool TryDequeue(out TElement element)
{
if (_heap.Count == 0)
{
element = default(TElement);
return false;
}
element = Dequeue();
return true;
}
public TElement Peek()
{
if (_heap.Count == 0)
{
throw new InvalidOperationException("The queue is empty.");
}
return _heap[0].Element;
}
// ReSharper disable once IdentifierTypo
private void HeapifyUp(int index)
{
while (index > 0)
{
var parentIndex = (index - 1) / 2;
if (_heap[index].Priority.CompareTo(_heap[parentIndex].Priority) >= 0)
{
break;
}
Swap(index, parentIndex);
index = parentIndex;
}
}
// ReSharper disable once IdentifierTypo
private void HeapifyDown(int index)
{
var lastIndex = _heap.Count - 1;
while (true)
{
var smallestIndex = index;
var leftChildIndex = 2 * index + 1;
var rightChildIndex = 2 * index + 2;
if (leftChildIndex <= lastIndex && _heap[leftChildIndex].Priority.CompareTo(_heap[smallestIndex].Priority) < 0)
{
smallestIndex = leftChildIndex;
}
if (rightChildIndex <= lastIndex && _heap[rightChildIndex].Priority.CompareTo(_heap[smallestIndex].Priority) < 0)
{
smallestIndex = rightChildIndex;
}
if (smallestIndex == index)
{
break;
}
Swap(index, smallestIndex);
index = smallestIndex;
}
}
private void Swap(int index1, int index2)
{
var temp = _heap[index1];
_heap[index1] = _heap[index2];
_heap[index2] = temp;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0fb73a1acb1843ad86127be8503ed92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
// ReSharper disable ConvertToPrimaryConstructor
// ReSharper disable SwapViaDeconstruction
// ReSharper disable InconsistentNaming
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace NBC.DataStructure.PriorityQueue
{
public struct PriorityQueueItemUint<T>
{
public T Element { get; set; }
public uint Priority { get; set; }
public PriorityQueueItemUint(T element, uint priority)
{
Element = element;
Priority = priority;
}
}
public struct PriorityQueueItem<T, T1>
{
public T Element { get; }
public T1 Priority { get; }
public PriorityQueueItem(T element, T1 priority)
{
Element = element;
Priority = priority;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79c4ab0531d874383b52d8b7082cf3a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,116 @@
// ReSharper disable SwapViaDeconstruction
// ReSharper disable UseIndexFromEndExpression
// ReSharper disable ConvertToPrimaryConstructor
using System;
using System.Collections.Generic;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8601 // Possible null reference assignment.
namespace NBC.DataStructure.PriorityQueue
{
public sealed class PriorityQueue<T> where T : IComparable<T>
{
private readonly List<T> _heap;
public PriorityQueue(int initialCapacity = 16)
{
_heap = new List<T>(initialCapacity);
}
public int Count => _heap.Count;
public void Enqueue(T item)
{
_heap.Add(item);
HeapifyUp(_heap.Count - 1);
}
public T Dequeue()
{
if (_heap.Count == 0)
{
throw new InvalidOperationException("The queue is empty.");
}
var item = _heap[0];
var heapCount = _heap.Count - 1;
_heap[0] = _heap[heapCount];
_heap.RemoveAt(heapCount);
HeapifyDown(0);
return item;
}
public bool TryDequeue(out T item)
{
if (_heap.Count == 0)
{
item = default(T);
return false;
}
item = Dequeue();
return true;
}
public T Peek()
{
if (_heap.Count == 0)
{
throw new InvalidOperationException("The queue is empty.");
}
return _heap[0];
}
// ReSharper disable once IdentifierTypo
private void HeapifyUp(int index)
{
while (index > 0)
{
var parentIndex = (index - 1) / 2;
if (_heap[index].CompareTo(_heap[parentIndex]) >= 0)
{
break;
}
Swap(index, parentIndex);
index = parentIndex;
}
}
// ReSharper disable once IdentifierTypo
private void HeapifyDown(int index)
{
var lastIndex = _heap.Count - 1;
while (true)
{
var smallestIndex = index;
var leftChildIndex = 2 * index + 1;
var rightChildIndex = 2 * index + 2;
if (leftChildIndex <= lastIndex && _heap[leftChildIndex].CompareTo(_heap[smallestIndex]) < 0)
{
smallestIndex = leftChildIndex;
}
if (rightChildIndex <= lastIndex && _heap[rightChildIndex].CompareTo(_heap[smallestIndex]) < 0)
{
smallestIndex = rightChildIndex;
}
if (smallestIndex == index)
{
break;
}
Swap(index, smallestIndex);
index = smallestIndex;
}
}
private void Swap(int index1, int index2)
{
var temp = _heap[index1];
_heap[index1] = _heap[index2];
_heap[index2] = temp;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c604a0410d104d68a5d1718a570e5c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 68c20f8b3fb7f461f9b2f22aa39b4c2e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,190 @@
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
namespace NBC.DataStructure.SkipTable
{
/// <summary>
/// 跳表数据结构(升序版)
/// </summary>
/// <typeparam name="TValue">跳表中存储的值的类型。</typeparam>
public class SkipTable<TValue> : SkipTableBase<TValue>
{
/// <summary>
/// 创建一个新的跳表实例。
/// </summary>
/// <param name="maxLayer">跳表的最大层数。</param>
public SkipTable(int maxLayer = 8) : base(maxLayer) { }
/// <summary>
/// 向跳表中添加一个新节点。
/// </summary>
/// <param name="sortKey">节点的主排序键。</param>
/// <param name="viceKey">节点的副排序键。</param>
/// <param name="key">节点的唯一键。</param>
/// <param name="value">要添加的值。</param>
public override void Add(long sortKey, long viceKey, long key, TValue value)
{
var rLevel = 1;
while (rLevel <= MaxLayer && Random.Next(3) == 0)
{
++rLevel;
}
SkipTableNode<TValue> cur = TopHeader, last = null;
for (var layer = MaxLayer; layer >= 1; --layer)
{
// 节点有next节点next主键 < 插入主键) 或 next主键 == 插入主键 且 next副键 < 插入副键)
while (cur.Right != null && ((cur.Right.SortKey < sortKey) ||
(cur.Right.SortKey == sortKey && cur.Right.ViceKey < viceKey)))
{
cur = cur.Right;
}
if (layer <= rLevel)
{
var currentRight = cur.Right;
// 在当前层插入新节点
cur.Right = new SkipTableNode<TValue>(sortKey, viceKey, key, value, layer == 1 ? cur.Index + 1 : 0, cur, cur.Right, null);
if (currentRight != null)
{
currentRight.Left = cur.Right;
}
if (last != null)
{
last.Down = cur.Right;
}
if (layer == 1)
{
// 更新索引信息
cur.Right.Index = cur.Index + 1;
Node.Add(key, cur.Right);
SkipTableNode<TValue> v = cur.Right.Right;
while (v != null)
{
v.Index++;
v = v.Right;
}
}
last = cur.Right;
}
cur = cur.Down;
}
}
/// <summary>
/// 从跳表中移除一个节点。
/// </summary>
/// <param name="sortKey">节点的主排序键。</param>
/// <param name="viceKey">节点的副排序键。</param>
/// <param name="key">节点的唯一键。</param>
/// <param name="value">被移除的节点的值。</param>
/// <returns>如果成功移除节点,则为 true否则为 false。</returns>
public override bool Remove(long sortKey, long viceKey, long key, out TValue value)
{
value = default;
var seen = false;
var cur = TopHeader;
for (var layer = MaxLayer; layer >= 1; --layer)
{
// 先按照主键查找 再 按副键查找
while (cur.Right != null && cur.Right.SortKey < sortKey && cur.Right.Key != key) cur = cur.Right;
while (cur.Right != null && (cur.Right.SortKey == sortKey && cur.Right.ViceKey <= viceKey) &&
cur.Right.Key != key) cur = cur.Right;
var isFind = false;
var currentCur = cur;
SkipTableNode<TValue> removeCur = null;
// 如果当前不是要删除的节点、但主键和副键都一样、需要特殊处理下。
if (cur.Right != null && cur.Right.Key == key)
{
isFind = true;
removeCur = cur.Right;
currentCur = cur;
}
else
{
// 先向左查找下
var currentNode = cur.Left;
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
{
if (currentNode.Key == key)
{
isFind = true;
removeCur = currentNode;
currentCur = currentNode.Left;
break;
}
currentNode = currentNode.Left;
}
// 再向右查找下
if (!isFind)
{
currentNode = cur.Right;
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
{
if (currentNode.Key == key)
{
isFind = true;
removeCur = currentNode;
currentCur = currentNode.Left;
break;
}
currentNode = currentNode.Right;
}
}
}
if (isFind && currentCur != null)
{
value = removeCur.Value;
currentCur.Right = removeCur.Right;
if (removeCur.Right != null)
{
removeCur.Right.Left = currentCur;
removeCur.Right = null;
}
removeCur.Left = null;
removeCur.Down = null;
removeCur.Value = default;
if (layer == 1)
{
var tempCur = currentCur.Right;
while (tempCur != null)
{
tempCur.Index--;
tempCur = tempCur.Right;
}
Node.Remove(removeCur.Key);
}
seen = true;
}
cur = cur.Down;
}
return seen;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 45fbcdc219ef843aaa31594eb4e76a5b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,282 @@
using System;
using System.Collections;
using System.Collections.Generic;
using NBC.DataStructure.Collection;
#pragma warning disable CS8601
#pragma warning disable CS8603
#pragma warning disable CS8625
#pragma warning disable CS8604
namespace NBC.DataStructure.SkipTable
{
/// <summary>
/// 抽象的跳表基类,提供跳表的基本功能和操作。
/// </summary>
/// <typeparam name="TValue">跳表中存储的值的类型。</typeparam>
public abstract class SkipTableBase<TValue> : IEnumerable<SkipTableNode<TValue>>
{
/// <summary>
/// 跳表的最大层数
/// </summary>
public readonly int MaxLayer;
/// <summary>
/// 跳表的顶部头节点
/// </summary>
public readonly SkipTableNode<TValue> TopHeader;
/// <summary>
/// 跳表的底部头节点
/// </summary>
public SkipTableNode<TValue> BottomHeader;
/// <summary>
/// 跳表中节点的数量,使用了 Node 字典的计数
/// </summary>
public int Count => Node.Count;
/// <summary>
/// 用于生成随机数的随机数生成器
/// </summary>
protected readonly Random Random = new Random();
/// <summary>
/// 存储跳表节点的字典
/// </summary>
protected readonly Dictionary<long, SkipTableNode<TValue>> Node = new();
/// <summary>
/// 用于辅助反向查找的栈
/// </summary>
protected readonly Stack<SkipTableNode<TValue>> AntiFindStack = new Stack<SkipTableNode<TValue>>();
/// <summary>
/// 初始化一个新的跳表实例。
/// </summary>
/// <param name="maxLayer">跳表的最大层数,默认为 8。</param>
protected SkipTableBase(int maxLayer = 8)
{
MaxLayer = maxLayer;
var cur = TopHeader = new SkipTableNode<TValue>(long.MinValue, 0, 0, default, 0, null, null, null);
for (var layer = MaxLayer - 1; layer >= 1; --layer)
{
cur.Down = new SkipTableNode<TValue>(long.MinValue, 0, 0, default, 0, null, null, null);
cur = cur.Down;
}
BottomHeader = cur;
}
/// <summary>
/// 获取指定键的节点的值,若不存在则返回默认值。
/// </summary>
/// <param name="key">要查找的键。</param>
public TValue this[long key] => !TryGetValueByKey(key, out TValue value) ? default : value;
/// <summary>
/// 获取指定键的节点在跳表中的排名。
/// </summary>
/// <param name="key">要查找的键。</param>
/// <returns>节点的排名。</returns>
public int GetRanking(long key)
{
if (!Node.TryGetValue(key, out var node))
{
return 0;
}
return node.Index;
}
/// <summary>
/// 获取指定键的反向排名,即在比该键更大的节点中的排名。
/// </summary>
/// <param name="key">要查找的键。</param>
/// <returns>反向排名。</returns>
public int GetAntiRanking(long key)
{
var ranking = GetRanking(key);
if (ranking == 0)
{
return 0;
}
return Count + 1 - ranking;
}
/// <summary>
/// 尝试通过键获取节点的值。
/// </summary>
/// <param name="key">要查找的键。</param>
/// <param name="value">获取到的节点的值,如果键不存在则为默认值。</param>
/// <returns>是否成功获取节点的值。</returns>
public bool TryGetValueByKey(long key, out TValue value)
{
if (!Node.TryGetValue(key, out var node))
{
value = default;
return false;
}
value = node.Value;
return true;
}
/// <summary>
/// 尝试通过键获取节点。
/// </summary>
/// <param name="key">要查找的键。</param>
/// <param name="node">获取到的节点,如果键不存在则为 <c>null</c>。</param>
/// <returns>是否成功获取节点。</returns>
public bool TryGetNodeByKey(long key, out SkipTableNode<TValue> node)
{
if (Node.TryGetValue(key, out node))
{
return true;
}
return false;
}
/// <summary>
/// 在跳表中查找节点,返回从起始位置到结束位置的节点列表。
/// </summary>
/// <param name="start">起始位置的排名。</param>
/// <param name="end">结束位置的排名。</param>
/// <param name="list">用于存储节点列表的 <see cref="ListPool{T}"/> 实例。</param>
public void Find(int start, int end, ListPool<SkipTableNode<TValue>> list)
{
var cur = BottomHeader;
var count = end - start;
for (var i = 0; i < start; i++)
{
cur = cur.Right;
}
for (var i = 0; i <= count; i++)
{
if (cur == null)
{
break;
}
list.Add(cur);
cur = cur.Right;
}
}
/// <summary>
/// 在跳表中进行反向查找节点,返回从结束位置到起始位置的节点列表。
/// </summary>
/// <param name="start">结束位置的排名。</param>
/// <param name="end">起始位置的排名。</param>
/// <param name="list">用于存储节点列表的 <see cref="ListPool{T}"/> 实例。</param>
public void AntiFind(int start, int end, ListPool<SkipTableNode<TValue>> list)
{
var cur = BottomHeader;
start = Count + 1 - start;
end = start - end;
for (var i = 0; i < start; i++)
{
cur = cur.Right;
if (cur == null)
{
break;
}
if (i < end)
{
continue;
}
AntiFindStack.Push(cur);
}
while (AntiFindStack.TryPop(out var node))
{
list.Add(node);
}
}
/// <summary>
/// 获取跳表中最后一个节点的值。
/// </summary>
/// <returns>最后一个节点的值。</returns>
public TValue GetLastValue()
{
var cur = TopHeader;
while (cur.Right != null || cur.Down != null)
{
while (cur.Right != null)
{
cur = cur.Right;
}
if (cur.Down != null)
{
cur = cur.Down;
}
}
return cur.Value;
}
/// <summary>
/// 移除跳表中指定键的节点。
/// </summary>
/// <param name="key">要移除的节点的键。</param>
/// <returns>移除是否成功。</returns>
public bool Remove(long key)
{
if (!Node.TryGetValue(key, out var node))
{
return false;
}
return Remove(node.SortKey, node.ViceKey, key, out _);
}
/// <summary>
/// 向跳表中添加节点。
/// </summary>
/// <param name="sortKey">节点的排序键。</param>
/// <param name="viceKey">节点的副键。</param>
/// <param name="key">节点的键。</param>
/// <param name="value">节点的值。</param>
public abstract void Add(long sortKey, long viceKey, long key, TValue value);
/// <summary>
/// 从跳表中移除指定键的节点。
/// </summary>
/// <param name="sortKey">节点的排序键。</param>
/// <param name="viceKey">节点的副键。</param>
/// <param name="key">节点的键。</param>
/// <param name="value">被移除的节点的值。</param>
/// <returns>移除是否成功。</returns>
public abstract bool Remove(long sortKey, long viceKey, long key, out TValue value);
/// <summary>
/// 返回一个枚举器,用于遍历跳表中的节点。
/// </summary>
/// <returns>一个可用于遍历跳表节点的枚举器。</returns>
public IEnumerator<SkipTableNode<TValue>> GetEnumerator()
{
var cur = BottomHeader.Right;
while (cur != null)
{
yield return cur;
cur = cur.Right;
}
}
/// <summary>
/// 返回一个非泛型枚举器,用于遍历跳表中的节点。
/// </summary>
/// <returns>一个非泛型枚举器,可用于遍历跳表节点。</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c70c703e84d842b9b488dd0798290d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,188 @@
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8601 // Possible null reference assignment.
namespace NBC.DataStructure.SkipTable
{
/// <summary>
/// 跳表降序版,用于存储降序排列的数据。
/// </summary>
/// <typeparam name="TValue">存储的值的类型。</typeparam>
public class SkipTableDesc<TValue> : SkipTableBase<TValue>
{
/// <summary>
/// 初始化跳表降序版的新实例。
/// </summary>
/// <param name="maxLayer">跳表的最大层数,默认为 8。</param>
public SkipTableDesc(int maxLayer = 8) : base(maxLayer) { }
/// <summary>
/// 向跳表中添加一个节点,根据降序规则进行插入。
/// </summary>
/// <param name="sortKey">排序主键。</param>
/// <param name="viceKey">副键。</param>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
public override void Add(long sortKey, long viceKey, long key, TValue value)
{
var rLevel = 1;
while (rLevel <= MaxLayer && Random.Next(3) == 0)
{
++rLevel;
}
SkipTableNode<TValue> cur = TopHeader, last = null;
for (var layer = MaxLayer; layer >= 1; --layer)
{
// 节点有next节点next主键 > 插入主键) 或 next主键 == 插入主键 且 next副键 > 插入副键)
while (cur.Right != null && ((cur.Right.SortKey > sortKey) ||
(cur.Right.SortKey == sortKey && cur.Right.ViceKey > viceKey)))
{
cur = cur.Right;
}
if (layer <= rLevel)
{
var currentRight = cur.Right;
cur.Right = new SkipTableNode<TValue>(sortKey, viceKey, key, value,
layer == 1 ? cur.Index + 1 : 0, cur, cur.Right, null);
if (currentRight != null)
{
currentRight.Left = cur.Right;
}
if (last != null)
{
last.Down = cur.Right;
}
if (layer == 1)
{
cur.Right.Index = cur.Index + 1;
Node.Add(key, cur.Right);
SkipTableNode<TValue> v = cur.Right.Right;
while (v != null)
{
v.Index++;
v = v.Right;
}
}
last = cur.Right;
}
cur = cur.Down;
}
}
/// <summary>
/// 从跳表中移除一个节点,根据降序规则进行移除。
/// </summary>
/// <param name="sortKey">排序主键。</param>
/// <param name="viceKey">副键。</param>
/// <param name="key">键。</param>
/// <param name="value">移除的节点值。</param>
/// <returns>如果成功移除节点,则返回 true否则返回 false。</returns>
public override bool Remove(long sortKey, long viceKey, long key, out TValue value)
{
value = default;
var seen = false;
var cur = TopHeader;
for (var layer = MaxLayer; layer >= 1; --layer)
{
// 先按照主键查找 再 按副键查找
while (cur.Right != null && cur.Right.SortKey > sortKey && cur.Right.Key != key) cur = cur.Right;
while (cur.Right != null && (cur.Right.SortKey == sortKey && cur.Right.ViceKey >= viceKey) &&
cur.Right.Key != key) cur = cur.Right;
var isFind = false;
var currentCur = cur;
SkipTableNode<TValue> removeCur = null;
// 如果当前不是要删除的节点、但主键和副键都一样、需要特殊处理下。
if (cur.Right != null && cur.Right.Key == key)
{
isFind = true;
removeCur = cur.Right;
currentCur = cur;
}
else
{
// 先向左查找下
var currentNode = cur.Left;
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
{
if (currentNode.Key == key)
{
isFind = true;
removeCur = currentNode;
currentCur = currentNode.Left;
break;
}
currentNode = currentNode.Left;
}
// 再向右查找下
if (!isFind)
{
currentNode = cur.Right;
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
{
if (currentNode.Key == key)
{
isFind = true;
removeCur = currentNode;
currentCur = currentNode.Left;
break;
}
currentNode = currentNode.Right;
}
}
}
if (isFind && currentCur != null)
{
value = removeCur.Value;
currentCur.Right = removeCur.Right;
if (removeCur.Right != null)
{
removeCur.Right.Left = currentCur;
removeCur.Right = null;
}
removeCur.Left = null;
removeCur.Down = null;
removeCur.Value = default;
if (layer == 1)
{
var tempCur = currentCur.Right;
while (tempCur != null)
{
tempCur.Index--;
tempCur = tempCur.Right;
}
Node.Remove(removeCur.Key);
}
seen = true;
}
cur = cur.Down;
}
return seen;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae15364981a674c59a2e20d2b99f097b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,68 @@
namespace NBC.DataStructure.SkipTable
{
/// <summary>
/// 跳跃表节点。
/// </summary>
/// <typeparam name="TValue">节点的值的类型。</typeparam>
public class SkipTableNode<TValue>
{
/// <summary>
/// 节点在跳跃表中的索引。
/// </summary>
public int Index;
/// <summary>
/// 节点的主键。
/// </summary>
public long Key;
/// <summary>
/// 节点的排序键。
/// </summary>
public long SortKey;
/// <summary>
/// 节点的副键。
/// </summary>
public long ViceKey;
/// <summary>
/// 节点存储的值。
/// </summary>
public TValue Value;
/// <summary>
/// 指向左侧节点的引用。
/// </summary>
public SkipTableNode<TValue> Left;
/// <summary>
/// 指向右侧节点的引用。
/// </summary>
public SkipTableNode<TValue> Right;
/// <summary>
/// 指向下一层节点的引用。
/// </summary>
public SkipTableNode<TValue> Down;
/// <summary>
/// 初始化跳跃表节点的新实例。
/// </summary>
/// <param name="sortKey">节点的排序键。</param>
/// <param name="viceKey">节点的副键。</param>
/// <param name="key">节点的主键。</param>
/// <param name="value">节点存储的值。</param>
/// <param name="index">节点在跳跃表中的索引。</param>
/// <param name="l">指向左侧节点的引用。</param>
/// <param name="r">指向右侧节点的引用。</param>
/// <param name="d">指向下一层节点的引用。</param>
public SkipTableNode(long sortKey, long viceKey, long key, TValue value, int index,
SkipTableNode<TValue> l,
SkipTableNode<TValue> r,
SkipTableNode<TValue> d)
{
Left = l;
Right = r;
Down = d;
Value = value;
Key = key;
Index = index;
SortKey = sortKey;
ViceKey = viceKey;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9384bd7a2a46f401ea6de3461b5aa36b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: