You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
5.1 KiB
C#
144 lines
5.1 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using LanLib.UpdateManager.Extensions;
|
|
|
|
namespace LanLib.UpdateManager.Internal
|
|
{
|
|
/// <summary>Generic list with O(1) insertion and removal.</summary>
|
|
/// <remarks>
|
|
/// <list type="bullet">
|
|
/// <item>Adding duplicate values and removing items that are not present are both no-op.</item>
|
|
/// <item>
|
|
/// Items may be added and removed while the collection is being enumerated without raising exceptions.
|
|
/// This collection guarantees that all items are enumerated, even if insertions or removals happen during enumeration.
|
|
/// </item>
|
|
/// <item>
|
|
/// All enumerators reference the same list index, so avoid having more than one enumerator at the same time.
|
|
/// Creating a new enumerator resets this shared index.
|
|
/// </item>
|
|
/// </list>
|
|
/// </remarks>
|
|
public class FastRemoveList<T> : IReadOnlyList<T>
|
|
{
|
|
private readonly List<T> _list = new List<T>();
|
|
private readonly Dictionary<T, int> _indexMap = new Dictionary<T, int>();
|
|
private int _loopIndex;
|
|
|
|
/// <returns>Get the number of items contained in the <see cref="FastRemoveList{T}"/>.</returns>
|
|
public int Count => _list.Count;
|
|
|
|
/// <returns>Item registered at <paramref name="index"/> if index is valid, <see langword="default"/> otherwise.</returns>
|
|
public T this[int index] => index >= 0 && index < Count ? _list[index] : default;
|
|
|
|
/// <summary>Add <paramref name="value"/> to the end of the list.</summary>
|
|
/// <returns><see langword="true"/> if value was not already present in the list and was successfully added, <see langword="false"/> otherwise.</returns>
|
|
/// <remarks>
|
|
/// Adding a value while the collection is being enumerated is permitted and won't raise exceptions.
|
|
/// This operation is O(1).
|
|
/// </remarks>
|
|
public bool Add(T value)
|
|
{
|
|
if (_indexMap.ContainsKey(value))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_list.Add(value);
|
|
_indexMap.Add(value, _list.Count - 1);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>Remove <paramref name="value"/> from the list, if found.</summary>
|
|
/// <returns><see langword="true"/> if value was present in the list and was successfully removed, <see langword="false"/> otherwise.</returns>
|
|
/// <remarks>
|
|
/// Removing a value while the collection is being enumerated is permitted and won't raise exceptions.
|
|
/// This operation is O(1).
|
|
/// </remarks>
|
|
public bool Remove(T value)
|
|
{
|
|
if (!_indexMap.TryGetValue(value, out int indexToRemove))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_indexMap.Remove(value);
|
|
|
|
// If removing the object that was just enumerated, make sure the
|
|
// new object swapped back to this index gets enumerated as well
|
|
if (indexToRemove == _loopIndex)
|
|
{
|
|
_loopIndex--;
|
|
}
|
|
// If removing an object that was already enumerated while the loop is
|
|
// still running, swap it with current loop index to make sure we
|
|
// still enumerate the last element that will be swapped back later
|
|
else if (indexToRemove < _loopIndex)
|
|
{
|
|
_list.Swap(_loopIndex, indexToRemove, out T swappedValue);
|
|
_indexMap[swappedValue] = indexToRemove;
|
|
indexToRemove = _loopIndex;
|
|
_loopIndex--;
|
|
}
|
|
|
|
_list.RemoveAtSwapBack(indexToRemove, out T swappedBack);
|
|
if (swappedBack != null)
|
|
{
|
|
_indexMap[swappedBack] = indexToRemove;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>Removes all values from the <see cref="FastRemoveList{T}"/>.</summary>
|
|
public void Clear()
|
|
{
|
|
_list.Clear();
|
|
_indexMap.Clear();
|
|
}
|
|
|
|
/// <summary>Returns an enumerator that iterates through the <see cref="FastRemoveList{T}"/>.</summary>
|
|
public Enumerator GetEnumerator()
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
public struct Enumerator : IEnumerator<T>
|
|
{
|
|
private FastRemoveList<T> _list;
|
|
|
|
public Enumerator(FastRemoveList<T> list)
|
|
{
|
|
_list = list;
|
|
Reset();
|
|
}
|
|
|
|
public T Current => _list[_list._loopIndex];
|
|
object IEnumerator.Current => Current;
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
public bool MoveNext()
|
|
{
|
|
if (_list._loopIndex < _list.Count - 1)
|
|
{
|
|
_list._loopIndex++;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
_list._loopIndex = -1;
|
|
}
|
|
}
|
|
}
|
|
} |