using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; namespace CustomCollections; /// /// A doubly-linked list with a Dictionary index. Duplicate items are not allowed. /// -Add is O(1) /// -Contains is O(1) /// -Remove is O(1) /// -Get/set by index is O(n) /// -Insert is O(n) /// -RemoveAt is O(n) /// Additionally, a cached pointer (with associated index) is kept pointing to the last used index item. /// When looking up an item by index, the list is walked from the head, tail, or cached index pointer. /// Thus, doing multiple operations in index order is O(1) even without an enumerator. /// /// internal class HashedList : IList, ICollection, IEnumerable, IEnumerable { private class ListNode { public ListNode Previous; public ListNode Next; public T Value; public ListNode(T v, ListNode prev, ListNode nxt) { Previous = prev; Next = nxt; Value = v; } } private ListNode Head; private ListNode Tail; private Dictionary HashIndex = new Dictionary(); private int ChangeMarker; private bool IndexCacheValid; private int IndexCache_Index; private ListNode IndexCache_Value; public T this[int index] { get { if (index >= HashIndex.Count || index < 0) { throw new ArgumentOutOfRangeException(); } return RunToIndex(index).Value; } set { if (index >= HashIndex.Count || index < 0) { throw new ArgumentOutOfRangeException(); } if (HashIndex.ContainsKey(value)) { throw new ArgumentException("Duplicate value"); } ListNode listNode = RunToIndex(index); HashIndex.Remove(listNode.Value); listNode.Value = value; HashIndex.Add(value, listNode); ChangeMarker++; } } public int Count => HashIndex.Count; public bool IsReadOnly => false; public HashedList() { } public HashedList(T[] arr) { foreach (T item in arr) { Add(item); } } /// /// This method gets the node corresponding to a particular index. To get there, /// the list is traversed from the head, tail, or cached index pointer (if valid). /// /// /// private ListNode RunToIndex(int ind) { int num = HashIndex.Count / 2; if (IndexCacheValid) { if (ind > IndexCache_Index) { if (ind - IndexCache_Index < num) { ListNode listNode = IndexCache_Value; for (int i = IndexCache_Index; i < ind; i++) { listNode = listNode.Next; } IndexCache_Index = ind; IndexCache_Value = listNode; IndexCacheValid = true; return listNode; } } else if (IndexCache_Index - ind < num) { ListNode listNode2 = IndexCache_Value; for (int num2 = IndexCache_Index; num2 > ind; num2--) { listNode2 = listNode2.Previous; } IndexCache_Index = ind; IndexCache_Value = listNode2; IndexCacheValid = true; return listNode2; } } if (ind < num) { ListNode listNode3 = Head; for (int j = 0; j < ind; j++) { listNode3 = listNode3.Next; } if (listNode3 == null) { throw new Exception(); } IndexCache_Index = ind; IndexCache_Value = listNode3; IndexCacheValid = true; return listNode3; } ListNode listNode4 = Tail; for (int num3 = HashIndex.Count - 1; num3 > ind; num3--) { listNode4 = listNode4.Previous; } if (listNode4 == null) { throw new Exception(); } IndexCache_Index = ind; IndexCache_Value = listNode4; IndexCacheValid = true; return listNode4; } private void RemoveNode(ListNode n) { if (n.Previous == null) { Head = n.Next; } else { n.Previous.Next = n.Next; } if (n.Next == null) { Tail = n.Previous; } else { n.Next.Previous = n.Previous; } HashIndex.Remove(n.Value); } public ReadOnlyCollection AsReadOnly() { return new ReadOnlyCollection(this); } public bool GetIfContains(T matchval, ref T outval) { if (HashIndex.ContainsKey(matchval)) { outval = HashIndex[matchval].Value; return true; } return false; } public int IndexOf(T item) { if (HashIndex.ContainsKey(item)) { ListNode listNode = HashIndex[item]; ListNode listNode2 = Head; for (int i = 0; i < HashIndex.Count; i++) { if (listNode2 == listNode) { return i; } listNode2 = listNode2.Next; } return -1; } return -1; } public void Insert(int index, T item) { if (index > HashIndex.Count || index < 0) { throw new ArgumentOutOfRangeException(); } if (HashIndex.ContainsKey(item)) { throw new ArgumentException("Duplicate value"); } ListNode listNode; if (index == HashIndex.Count) { Add(item); listNode = HashIndex[item]; } else if (index == 0) { listNode = new ListNode(item, null, Head); Head.Previous = listNode; Head = listNode; HashIndex.Add(item, listNode); } else { ListNode listNode2 = RunToIndex(index - 1); listNode = (listNode2.Next = new ListNode(item, listNode2, listNode2.Next)); listNode.Next.Previous = listNode; HashIndex.Add(item, listNode); } ChangeMarker++; IndexCache_Index = index; IndexCache_Value = listNode; IndexCacheValid = true; } public void RemoveAt(int index) { if (index >= HashIndex.Count || index < 0) { throw new ArgumentOutOfRangeException(); } ListNode n = RunToIndex(index); RemoveNode(n); ChangeMarker++; IndexCacheValid = false; } public void Add(T item) { ListNode listNode; if (HashIndex.Count == 0) { listNode = (Tail = (Head = new ListNode(item, null, null))); } else { if (HashIndex.ContainsKey(item)) { throw new ArgumentException("Duplicate value"); } listNode = new ListNode(item, Tail, null); Tail.Next = listNode; Tail = listNode; } HashIndex.Add(item, listNode); ChangeMarker++; IndexCacheValid = false; } public void Clear() { Head = null; Tail = null; HashIndex.Clear(); ChangeMarker++; IndexCacheValid = false; } public bool Contains(T item) { return HashIndex.ContainsKey(item); } public bool Remove(T item) { if (!HashIndex.ContainsKey(item)) { return false; } RemoveNode(HashIndex[item]); ChangeMarker++; IndexCacheValid = false; return true; } public void CopyTo(T[] array, int arrayIndex) { int num = arrayIndex + HashIndex.Count; IEnumerator enumerator = GetEnumerator(); for (int i = arrayIndex; i < num; i++) { enumerator.MoveNext(); array[i] = enumerator.Current; } } public T[] ToArray() { T[] array = new T[HashIndex.Count]; CopyTo(array, 0); return array; } public IEnumerator GetEnumerator() { int startchangemarker = ChangeMarker; for (ListNode cur = Head; cur != null; cur = cur.Next) { yield return cur.Value; if (ChangeMarker != startchangemarker) { throw new Exception("Collection modified during enumeration"); } } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }