chore: added an experimental ordered fast list class
This commit is contained in:
172
Engine.Core/Helpers/FastListOrdered.cs
Normal file
172
Engine.Core/Helpers/FastListOrdered.cs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TODO This is VEERY experimental, and doesn't work well with the indices access. Use with caution
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TIndex"></typeparam>
|
||||||
|
/// <typeparam name="TItem"></typeparam>
|
||||||
|
public class FastListOrdered<TIndex, TItem> : IList<TItem>, IReadOnlyList<TItem>, IEnumerable<TItem> where TItem : notnull where TIndex : IComparable
|
||||||
|
{
|
||||||
|
private readonly SortedDictionary<TIndex, FastList<TItem>> items = null!;
|
||||||
|
|
||||||
|
private readonly Func<TItem, TIndex> getIndexFunc = null!;
|
||||||
|
private readonly IComparer<TIndex> sortBy = null!;
|
||||||
|
|
||||||
|
private int count = 0;
|
||||||
|
public int Count => count;
|
||||||
|
|
||||||
|
public bool IsReadOnly { get; set; } = false;
|
||||||
|
|
||||||
|
public TItem this[int index]
|
||||||
|
{
|
||||||
|
get { (TIndex tIndex, int i) = GetAt(index); return items[tIndex][i]; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
(TIndex tIndex, int i) = GetAt(index); items[tIndex][i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private (TIndex TIndex, int i) GetAt(Index index)
|
||||||
|
{
|
||||||
|
int actualIndex = index.IsFromEnd
|
||||||
|
? count - index.Value
|
||||||
|
: index.Value;
|
||||||
|
|
||||||
|
if (actualIndex < 0 || actualIndex >= count)
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
int leftIndex = actualIndex;
|
||||||
|
foreach ((TIndex i, FastList<TItem> list) in items)
|
||||||
|
{
|
||||||
|
if (leftIndex < list.Count)
|
||||||
|
return (i, leftIndex);
|
||||||
|
leftIndex -= list.Count;
|
||||||
|
}
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(TItem item)
|
||||||
|
{
|
||||||
|
int indexCounter = 0;
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
{
|
||||||
|
int i = list.IndexOf(item);
|
||||||
|
if (i != -1)
|
||||||
|
return indexCounter + i;
|
||||||
|
indexCounter += list.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TItem item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
TIndex key = getIndexFunc(item);
|
||||||
|
if (!items.TryGetValue(key, out FastList<TItem>? list))
|
||||||
|
items[key] = list = [];
|
||||||
|
|
||||||
|
list.Add(item);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, TItem item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
TIndex tIndex = getIndexFunc(item);
|
||||||
|
if (!items.TryGetValue(tIndex, out FastList<TItem>? list))
|
||||||
|
items[tIndex] = list = [];
|
||||||
|
|
||||||
|
list.Insert(index, item);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(TItem item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
TIndex index = getIndexFunc(item);
|
||||||
|
if (!items.TryGetValue(index, out FastList<TItem>? list))
|
||||||
|
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
|
||||||
|
|
||||||
|
if (!list.Remove(item))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
count--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
(TIndex tIndex, int i) = GetAt(index);
|
||||||
|
items[tIndex].RemoveAt(i);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TItem item)
|
||||||
|
{
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
if (list.Contains(item))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(TItem[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
int indexCounter = 0;
|
||||||
|
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
{
|
||||||
|
list.CopyTo(array, indexCounter);
|
||||||
|
indexCounter += list.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<TItem> GetEnumerator()
|
||||||
|
{
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
foreach (TItem item in list)
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public FastListOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
|
||||||
|
{
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = Comparer<TIndex>.Create(sortBy);
|
||||||
|
items = new(this.sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastListOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
|
||||||
|
{
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = sortBy;
|
||||||
|
items = new(sortBy);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user