/// <summary> /// Expands the table to the next good capacity. /// This will expand to the capacity GetNextLength(table.Capacity). /// See: GetNextLength. /// </summary> public static void Expand <TKey, TValue>(SimpleTable <TKey, TValue> table) { var oldCapacity = table.Capacity; var oldEntries = table.Entries; var newCapacity = GetNextLength(oldCapacity); var newArrayEntry = new TableEntry <TKey, TValue> [newCapacity]; table.Capacity = newCapacity; table.Entries = newArrayEntry; table.Count = 0; for (int i = 0; i < oldCapacity; i++) { var entry = oldEntries[i]; if (entry != null) { Add(table, entry.Key, entry.Value, false); while (entry.Next != null) { entry = entry.Next; Add(table, entry.Key, entry.Value, false); } } } }
/// <summary> /// Remove all elements of the table. /// </summary> public static void Clear <TKey, TValue>(SimpleTable <TKey, TValue> table) { for (int i = 0; i < table.Capacity; i++) { var entry = table.Entries[i]; if (entry == null) { continue; } table.Entries[i] = null; entry.Key = default(TKey); entry.Value = default(TValue); entry.HashCode = -1; SList.Add(table.EntryPool, entry); while (entry.Next != null) { entry = entry.Next; entry.Key = default(TKey); entry.Value = default(TValue); entry.HashCode = -1; SList.Add(table.EntryPool, entry); } } table.Count = 0; }
/// <summary> /// Finds the value that corresponds to the given key. /// Returns the value for the key or null if the key is not on the dictionary. /// </summary> public static TValue Find <TKey, TValue>(SimpleTable <TKey, TValue> table, TKey key) { var hash = table.Comparer.GetHashCode(key); var index = (hash & int.MaxValue) % table.Capacity; var entry = table.Entries[index]; if (entry != null) { if (entry.HashCode == hash && table.Comparer.Equals(entry.Key, key)) { return(entry.Value); } else { while (entry.Next != null) { entry = entry.Next; if (entry.HashCode == hash && table.Comparer.Equals(entry.Key, key)) { return(entry.Value); } } } } return(default(TValue)); }
/// <summary> /// Set the comparer to use when getting the hash code. /// If comparer is null, the current comparer is not changed. /// </summary> public static void SetComparer <TKey, TValue>(SimpleTable <TKey, TValue> table, IEqualityComparer <TKey> comparer) { if (comparer != null) { table.Comparer = comparer; } }
/// <summary> /// Creates a new table entry with the given values. /// </summary> private static TableEntry <TKey, TValue> CreateEntry <TKey, TValue>(SimpleTable <TKey, TValue> table, TKey key, TValue value, int hash) { TableEntry <TKey, TValue> newEntry; if (table.EntryPool.Count > 0) { newEntry = table.EntryPool[table.EntryPool.Count - 1]; SList.RemoveLast(table.EntryPool); // remove last for better perfomance } else { newEntry = new TableEntry <TKey, TValue>(); } newEntry.Key = key; newEntry.Value = value; newEntry.HashCode = hash; return(newEntry); }
/// <summary> /// Removes the given item that corresponds to the given key. /// </summary> public static void Remove <TKey, TValue>(SimpleTable <TKey, TValue> table, TKey key) { var hash = table.Comparer.GetHashCode(key); var index = (hash & int.MaxValue) % table.Capacity; var entry = table.Entries[index]; if (entry == null) { return; } if (entry.HashCode == hash && table.Comparer.Equals(entry.Key, key)) { table.Entries[index] = null; table.Count--; entry.Key = default(TKey); entry.Value = default(TValue); entry.HashCode = -1; SList.Add(table.EntryPool, entry); } else { while (entry.Next != null) { if (entry.Next.HashCode == hash && table.Comparer.Equals(entry.Next.Key, key)) { var removed = entry.Next; entry.Next = entry.Next.Next; removed.Key = default(TKey); removed.Value = default(TValue); removed.HashCode = -1; SList.Add(table.EntryPool, removed); table.Count--; } entry = entry.Next; } } }
/// <summary> /// Creates a fake hash table with the given capacity. /// This will also create an pool of TableEntry (the internal class used to store elements /// on the array) to reduce garbage generation and improve performance. /// If preWarmPool is true, this will pre-instantiate the elements on the pool. /// If preWarmPool is false, this will only create a SimpleSet of size 'size' and /// pool removed elements from the table for later reuse. /// </summary> public static SimpleTable <TKey, TValue> Create <TKey, TValue>(int size, bool preWarmPool) { var table = new SimpleTable <TKey, TValue>(); table.Count = 0; table.Capacity = GetNextLength(size); table.Entries = new TableEntry <TKey, TValue> [table.Capacity]; table.Comparer = EqualityComparer <TKey> .Default; table.Enumerator = new SimpleTableEnumerator <TKey, TValue>(); table.Enumerator.Table = table; table.EntryPool = SList.Create <TableEntry <TKey, TValue> >(size); if (preWarmPool) { for (int i = 0; i < size; i++) { table.EntryPool[i] = new TableEntry <TKey, TValue>(); } } return(table); }
/// <summary> /// Returns a enumerator that can be used to iterate over the table. /// </summary> public static IEnumerator <Pair <TKey, TValue> > GetEnumerator <TKey, TValue>(SimpleTable <TKey, TValue> table) { table.Enumerator.Reset(); return(table.Enumerator); }
/// <summary> /// Tries to get the value. Returns true if found, false otherwise. /// If this returns true, the value parameter will be the found value, otherwise it will be null. /// </summary> public static bool TryGetValue <TKey, TValue>(SimpleTable <TKey, TValue> table, TKey key, out TValue value) { value = Find(table, key); return(value != null); }
/// <summary> /// Adds the value to the table using the key. /// If replace is true and the key is already present on the table, we will replace the value, /// if false, we will return false to signal the failure. /// </summary> public static bool Add <TKey, TValue>(SimpleTable <TKey, TValue> table, TKey key, TValue value, bool replace) { var hash = table.Comparer.GetHashCode(key); var index = (hash & int.MaxValue) % table.Capacity; var inserted = false; var entry = table.Entries[index]; if (entry != null) { // If we find a entry with the given key, validate if it's the same key // this is needed because two keys can produce the same hash if (entry.HashCode == hash && table.Comparer.Equals(key, entry.Key)) { if (replace) { entry.Value = value; } else { #if DEBUG var msg = string.Format("ADDING THE KEY {0} TWICE TO THE SIMPLE TABLE!", key); System.Diagnostics.Debug.Assert(true, msg); #if USE_UNITY UnityEngine.Debug.LogError(msg); #endif #endif } } else { // It's a collision, create an entry and add to the linked list of collisions var newEntry = CreateEntry(table, key, value, hash); // Find the last element of the collision chain while (entry.Next != null) { entry = entry.Next; } entry.Next = newEntry; inserted = true; if (table.Count >= table.Capacity) { Expand(table); } } } // Did not found, create and add an entry else { var newEntry = CreateEntry(table, key, value, hash); if (table.Count >= table.Capacity) { Expand(table); } table.Entries[index] = newEntry; inserted = true; } if (inserted) { table.Count++; } return(inserted); }