Jagged Array Collection Class (No Copy Resize)
Updated: Jun-21,2021
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; /// <summary> /// Uses a multidimensional array to create a growable array with out copy or re-allocation. /// </summary> [DebuggerDisplay("Count = {Count}")] [Serializable] public struct JaggedArray<T> : IEnumerable<T>, IDisposable { private const int FirstDimensionMax = 4096; public const int ShiftCount = 19; public const int Granularity = 1 << ShiftCount; private bool _disposed; private T[][] _array; public int Count; public int Length; public int NumberOfActiveArrays; public bool Allocated; public JaggedArray(int size) { if (size < Granularity) size = Granularity; try { NumberOfActiveArrays = (size + (Granularity - 1)) / Granularity; Length = NumberOfActiveArrays * Granularity; _array = new T[FirstDimensionMax][]; for (var i = 0; i < NumberOfActiveArrays; ++i) _array[i] = new T[Granularity]; } catch (Exception ex) { throw new Exception($"Exception: {ex.Message}"); } Count = 0; _disposed = false; Allocated = true; } public T this[int index] { get { if (index >= Length) throw new Exception($"Getter: Index out of bounds, Index: '{index}' must be less than the Length: '{Length}'."); return _array[index >> ShiftCount][index & (Granularity - 1)]; } set { if (index >= Length) ResizeArray(); _array[index >> ShiftCount][index & (Granularity - 1)] = value; Count++; } } public void Dispose() { Dispose(true); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(T Item) { if (Count >= Length) ResizeArray(); var x = Count >> ShiftCount; var y = Count & (Granularity - 1); _array[x][y] = Item; Count++; } private void ResizeArray() { try { _array[NumberOfActiveArrays] = new T[Granularity]; NumberOfActiveArrays++; } catch (Exception ex) { throw new Exception($"Exception: {ex.Message}"); } } public void Zero() { for (var a = 0L; a < NumberOfActiveArrays; a++) _array[a] = Array.Empty<T>(); Count = 0; } public void Clear() { for (var a = 0L; a < NumberOfActiveArrays; a++) Array.Clear(_array[a], 0, Granularity); Count = 0; } public int IndexOf(T item) { var i = 0; for (; i < NumberOfActiveArrays; i++) { var pos = Array.IndexOf(_array[i], item, 0); if (pos != -1) return i * Granularity + pos; } return -1; } public JaggedArray<T> Copy(int newsize) { var temp = new JaggedArray<T>(newsize); for (var a = 0L; a < NumberOfActiveArrays; a++) Array.Copy(_array[a], temp._array[a], Granularity); temp.Count = Count; return temp; } public void FromArray(T[][] array) { NumberOfActiveArrays = array.GetUpperBound(0) + 1; Length = NumberOfActiveArrays * Granularity; _array = new T[NumberOfActiveArrays][]; for (var i = 0; i < NumberOfActiveArrays; ++i) _array[i] = new T[Granularity]; for (var a = 0L; a < NumberOfActiveArrays; a++) Array.Copy(array[a], _array[a], Granularity); } public T[][] ToRawArray() { var ta = new T[NumberOfActiveArrays][]; for (var i = 0; i < NumberOfActiveArrays; ++i) ta[i] = new T[Granularity]; for (var a = 0L; a < NumberOfActiveArrays; a++) Array.Copy(_array[a], ta[a], Granularity); return ta; } public T[] ToArray() { var newArray = new T[Count]; using (var en = GetEnumerator()) { var ptr = 0; while (en.MoveNext()) { var value = en.Current; if (value == null) break; newArray[ptr++] = value; } return newArray; } } private void Dispose(bool disposing) { if (!_disposed) if (disposing) _array = null; _disposed = true; } public IEnumerator<T> GetEnumerator() { return GetEnum(); } public IEnumerator<T> GetEnum() { for (var i = 0; i < Count; i++) yield return this[i]; } }