Last active
July 4, 2024 16:18
-
-
Save st4rdog/954506f3f0076b37baac2e21c364bc5a to your computer and use it in GitHub Desktop.
Unity C# - Garbage-free List with Add/Remove functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Drop-in replacement for List<T>. Presized List so no garbage when adding/removing. | |
// Further reading: | |
// - Collections without the boxing - https://www.jacksondunstan.com/articles/5148 | |
// - https://stackoverflow.com/questions/3737997/why-implement-ienumerablet-if-i-can-just-define-one-getenumerator | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class ListNonAlloc<T> : IEnumerable<T> | |
{ | |
private T[] _array; | |
private int _count; | |
private int _expandSize; | |
private ListNonAllocEnumerator _enumerator; | |
private class ListNonAllocEnumerator : IEnumerator<T> | |
{ | |
private ListNonAlloc<T> _list; | |
private int _currentIndex = -1; | |
public ListNonAllocEnumerator(ListNonAlloc<T> list) | |
{ | |
_list = list; | |
} | |
public T Current => _list[_currentIndex]; | |
object IEnumerator.Current => Current; | |
public bool MoveNext() | |
{ | |
_currentIndex++; | |
return _currentIndex < _list.Count; | |
} | |
public void Reset() | |
{ | |
_currentIndex = -1; | |
} | |
public void Dispose() | |
{ | |
// Dispose resources if needed | |
} | |
} | |
public IEnumerator<T> GetEnumerator() | |
{ | |
_enumerator.Reset(); | |
return _enumerator; | |
} | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
/// <param name="expandSize">How much the array will expand when adding at max capacity.</param> | |
public ListNonAlloc(int capacity, int expandSize = 20) | |
{ | |
_array = new T[capacity]; | |
_count = 0; | |
_expandSize = expandSize; | |
_enumerator = new ListNonAllocEnumerator(this); | |
} | |
public int Count => _count; | |
public int Capacity => _array.Length; | |
public void Add(T item) | |
{ | |
if (_count < _array.Length) | |
{ | |
_array[_count] = item; | |
_count++; | |
} | |
else | |
{ | |
// Auto-expand | |
var newCapacity = _array.Length + _expandSize; | |
var newArray = new T[newCapacity]; | |
_array.AsSpan().CopyTo(newArray); | |
_array = newArray; | |
_array[_count] = item; | |
_count++; | |
} | |
} | |
public void Remove(T item) | |
{ | |
int index = -1; | |
for (int i = 0; i < _count; i++) | |
{ | |
if (EqualityComparer<T>.Default.Equals(_array[i], item)) | |
{ | |
index = i; | |
break; | |
} | |
} | |
if (index >= 0) | |
{ | |
// Shift elements to fill the gap | |
for (int i = index; i < _count - 1; i++) | |
{ | |
_array[i] = _array[i + 1]; | |
} | |
_count--; | |
} | |
} | |
public void RemoveAt(int index) | |
{ | |
if (index >= 0 && index < _count) | |
{ | |
// Shift elements to fill the gap | |
for (int i = index; i < _count - 1; i++) | |
{ | |
_array[i] = _array[i + 1]; | |
} | |
_count--; | |
} | |
else | |
{ | |
// TODO: Optionally handle the case where the index is out of bounds | |
Debug.LogError("Index out of bounds."); | |
} | |
} | |
public bool Contains(T item) | |
{ | |
for (int i = 0; i < _count; i++) | |
{ | |
if (EqualityComparer<T>.Default.Equals(_array[i], item)) | |
{ | |
return true; | |
} | |
} | |
return false; | |
} | |
public T this[int index] | |
{ | |
get | |
{ | |
if (index >= 0 && index < _count) | |
{ | |
return _array[index]; | |
} | |
else | |
{ | |
// Optionally handle the case where the index is out of bounds | |
Debug.LogError("Index out of bounds."); | |
return default; | |
} | |
} | |
set | |
{ | |
if (index >= 0 && index < _count) | |
{ | |
_array[index] = value; | |
} | |
else | |
{ | |
// Optionally handle the case where the index is out of bounds | |
Debug.LogError("Index out of bounds."); | |
} | |
} | |
} | |
public void Clear() | |
{ | |
_count = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment