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