Implement remaining `IList<T>` methods on `SortedList<T>`

This commit is contained in:
YoshiRulz 2025-02-15 05:01:31 +10:00
parent 33571d4cd3
commit 5a236fdbee
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
2 changed files with 69 additions and 2 deletions

View File

@ -3,9 +3,11 @@ using System.Collections.Generic;
namespace BizHawk.Common
{
public class SortedList<T> : ICollection<T>
public class SortedList<T> : IList<T>
where T : IComparable<T>
{
private const string ERR_MSG_OUT_OF_ORDER = "setting/inserting elements must preserve ordering";
protected readonly List<T> _list;
public virtual int Count => _list.Count;
@ -20,7 +22,24 @@ namespace BizHawk.Common
_list.Sort();
}
public virtual T this[int index] => _list[index];
public virtual T this[int index]
{
get => _list[index];
set
{
// NOT allowing appends, to match BCL `List<T>`
if (index < 0 || Count <= index) throw new ArgumentOutOfRangeException(paramName: nameof(index), index, message: $"index must be in 0..<{Count}");
if (Count is 0)
{
_list.Add(value);
return;
}
var willBeGeqPrevious = index is 0 || value.CompareTo(_list[index - 1]) >= 0;
var willBeLeqFollowing = index == Count - 1 || _list[index + 1].CompareTo(value) >= 0;
if (willBeGeqPrevious && willBeLeqFollowing) _list[index] = value;
else throw new NotSupportedException(ERR_MSG_OUT_OF_ORDER);
}
}
public virtual void Add(T item)
{
@ -55,6 +74,21 @@ namespace BizHawk.Common
return i < 0 ? -1 : i;
}
public virtual void Insert(int index, T item)
{
// allowing appends per `IList<T>` docs
if (index < 0 || Count < index) throw new ArgumentOutOfRangeException(paramName: nameof(index), index, message: $"index must be in 0..{Count}");
if (Count is 0)
{
_list.Add(item);
return;
}
var willBeGeqPrevious = index is 0 || item.CompareTo(_list[index - 1]) >= 0;
var willBeLeqFollowing = index >= Count - 1 || _list[index].CompareTo(item) >= 0;
if (willBeGeqPrevious && willBeLeqFollowing) _list.Insert(index, item);
else throw new NotSupportedException(ERR_MSG_OUT_OF_ORDER);
}
public T LastOrDefault()
=> _list.Count is 0 ? default! : _list[_list.Count - 1];

View File

@ -26,6 +26,23 @@ namespace BizHawk.Tests.Common.CustomCollections
Assert.IsTrue(list.Contains(11)); // `Contains` when `BinarySearch` returns non-negative
}
[TestMethod]
public void TestSortedListInsert()
{
SortedList<int> list = new([ 1, 4, 7 ]);
Assert.ThrowsException<NotSupportedException>(() => list.Insert(index: 3, item: 0), "setting [^0] (appending) out-of-order should throw");
list.Insert(index: 3, item: 10);
Assert.IsTrue(list.SequenceEqual([ 1, 4, 7, 10 ]), "expecting [ 1, 4, 7, 10 ]");
Assert.ThrowsException<NotSupportedException>(() => list.Insert(index: 3, item: 0), "setting [^1] out-of-order should throw");
list.Insert(index: 3, item: 9);
Assert.IsTrue(list.SequenceEqual([ 1, 4, 7, 9, 10 ]), "expecting [ 1, 4, 7, 9, 10 ]");
Assert.ThrowsException<NotSupportedException>(() => list.Insert(index: 1, item: 9), "setting [1] out-of-order should throw");
list.Insert(index: 1, item: 3);
Assert.IsTrue(list.SequenceEqual([ 1, 3, 4, 7, 9, 10 ]), "expecting [ 1, 3, 4, 7, 9, 10 ]");
Assert.ThrowsException<NotSupportedException>(() => list.Insert(index: 0, item: 9), "setting [0] out-of-order should throw");
list.Insert(index: 0, item: 0);
Assert.IsTrue(list.SequenceEqual([ 0, 1, 3, 4, 7, 9, 10 ]), "expecting [ 0, 1, 3, 4, 7, 9, 10 ]");
}
[TestMethod]
[DataRow(new[] {1, 5, 9, 10, 11, 12}, new[] {1, 5, 9}, 9)]
@ -37,5 +54,21 @@ namespace BizHawk.Tests.Common.CustomCollections
sortlist.RemoveAfter(removeItem);
Assert.IsTrue(sortlist.ToArray().SequenceEqual(after));
}
[TestMethod]
public void TestSortedListSetIndexer()
{
SortedList<int> list = new([ 1, 3, 4 ]);
Assert.ThrowsException<NotSupportedException>(() => list[1] = 9, "setting [1] out-of-order should throw");
list[1] = 2;
Assert.IsTrue(list.SequenceEqual([ 1, 2, 4 ]), "expecting [ 1, 2, 4 ]");
Assert.ThrowsException<NotSupportedException>(() => list[0] = 9, "setting [0] out-of-order should throw");
list[0] = 0;
Assert.ThrowsException<NotSupportedException>(() => list[2] = 0, "setting [^1] out-of-order should throw");
list[2] = 9;
Assert.ThrowsException</*NotSupportedException*/ArgumentOutOfRangeException>(() => list[3] = 0, "setting [^0] (appending) out-of-order should throw");
Assert.ThrowsException<ArgumentOutOfRangeException>(() => list[3] = 10, "setting [^0] (appending) properly should throw"); // to match BCL `List<T>`
Assert.IsTrue(list.SequenceEqual([ 0, 2, 9 ]), "expecting [ 0, 2, 9 ]");
}
}
}