C# Unity A dictionary with one Key corresponding to multiple values
1. Purpose
C# does not provide a container with a key corresponding to multiple values. You need to encapsulate it yourself. There are many ways to implement it. You can use a linked list to implement it (just divide a linked list into regions). However, this is more complicated, but frequent insertion is better than using a List. Better ones do not need to dynamically expand the length. If you have the opportunity, you can share how to use a linked list to achieve a more efficient way of one Key corresponding to multiple values. Today I will share a simple, quick and easy to understand method.
2.OneToManyDic
c# uses a dictionary that provides a key corresponding to a value, and can be expanded on it (inherit Dictionary)
public class OneToManyDic<TKey, TValue> : Dictionary<TKey, List<TValue>> where TKey : notnull
In order to avoid frequent creation of List during Remove, use Queue queue to cache List.
private readonly Queue<List<TValue>> _cacheQueue = new Queue<List<TValue>>(); // 缓存list
private readonly int _recyclingLimit = 120;
public OneToManyDic()
{
}
/// <summary>
/// 设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量,全部缓存
/// </param>
public OneToManyDic(int recyclingLimit)
{
_recyclingLimit = recyclingLimit;
}
3. Complete code
public class OneToManyDic<TKey, TValue> : Dictionary<TKey, List<TValue>> where TKey : notnull
{
private readonly Queue<List<TValue>> _cacheQueue = new Queue<List<TValue>>(); // 缓存list
private readonly int _recyclingLimit = 120;
public OneToManyDic()
{
}
/// <summary>
/// 设置最大缓存数量
/// </summary>
/// <param name="recyclingLimit">
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
/// 2:设置成0不控制数量,全部缓存
/// </param>
public OneToManyDic(int recyclingLimit)
{
_recyclingLimit = recyclingLimit;
}
public bool Contains(TKey key, TValue value)
{
TryGetValue(key, out var list);
return list != null && list.Contains(value);
}
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);
}
public TValue? First(TKey key)
{
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
}
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;
}
public bool RemoveByKey(TKey key)
{
if (!TryGetValue(key, out var list))
{
return false;
}
Remove(key);
Recycle(list);
return true;
}
public List<TValue>? GetValues(TKey key)
{
return TryGetValue(key, out var list) ? list : null;
}
public new void Clear()
{
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
base.Clear();
}
private List<TValue> Fetch()
{
return _cacheQueue.Count <= 0 ? new List<TValue>() : _cacheQueue.Dequeue();
}
private void Recycle(List<TValue> list)
{
list.Clear();
if (_recyclingLimit != 0 && _cacheQueue.Count > _recyclingLimit) return;
_cacheQueue.Enqueue(list);
}
}
3. Test the code
OneToManyDic<int, string> dic = new OneToManyDic<int, string>();
dic.Add(1, "zzs");
dic.Add(1, "666");
dic.Add(2, "777");
dic.Add(2, "888");
dic.Add(2, "999");
Console.WriteLine(dic.Count);
Console.WriteLine(dic.GetValues(1)?.Count);
Console.WriteLine(dic.GetValues(2)?.Count);
Console.ReadKey();