optimized hashmap implementation in csharp

To optimize a HashMap implementation in C#, you can consider the following factors:

  1. Load factor: A load factor is the ratio of the number of elements stored in a hashmap to its capacity. Set a reasonable load factor to avoid frequent resizing of the internal array that backs the hashmap.

  2. Hash function: A hash function determines which bucket an element is stored in. Implement a good hashing algorithm that evenly distributes elements among buckets. Avoid collisions (multiple elements hashing to the same bucket) as much as possible.

  3. Internal data structure: Use an array of linked lists to store elements in each bucket. If the number of elements in a bucket exceeds a threshold, switch from a linked list to a binary tree to maintain O(log n) search and insertion time.

  4. Resizing: When the load factor exceeds a certain threshold, resize the internal array to accommodate more elements. Resizing involves rehashing all elements to new bucket indices, which can be expensive. Set a good initial capacity and load factor to minimize the number of resizes.

  5. Iteration: Provide efficient means of iterating over all elements in the hashmap, such as implementing the IEnumerable interface. Avoid creating unnecessary copies of elements or internal data structures during iteration.

Here's an example of an optimized HashMap implementation in C#:

main.cs
public class HashMap<K, V>
{
    private const int InitialCapacity = 16;
    private const float LoadFactor = 0.75f;

    private LinkedList<KeyValuePair<K, V>>[] buckets;
    private int count;

    public HashMap()
    {
        this.buckets = new LinkedList<KeyValuePair<K, V>>[InitialCapacity];
    }

    public void Add(K key, V value)
    {
        int bucketIndex = GetBucketIndex(key);
        if (this.buckets[bucketIndex] == null)
        {
            this.buckets[bucketIndex] = new LinkedList<KeyValuePair<K, V>>();
        }

        foreach (KeyValuePair<K, V> pair in this.buckets[bucketIndex])
        {
            if (pair.Key.Equals(key))
            {
                throw new ArgumentException("An element with the same key already exists in the hashmap.");
            }
        }

        this.buckets[bucketIndex].AddLast(new KeyValuePair<K, V>(key, value));
        this.count++;

        if (this.count > this.buckets.Length * LoadFactor)
        {
            Resize();
        }
    }

    public V Get(K key)
    {
        int bucketIndex = GetBucketIndex(key);
        if (this.buckets[bucketIndex] == null)
        {
            throw new KeyNotFoundException();
        }

        foreach (KeyValuePair<K, V> pair in this.buckets[bucketIndex])
        {
            if (pair.Key.Equals(key))
            {
                return pair.Value;
            }
        }

        throw new KeyNotFoundException();
    }

    public bool ContainsKey(K key)
    {
        int bucketIndex = GetBucketIndex(key);
        if (this.buckets[bucketIndex] == null)
        {
            return false;
        }

        foreach (KeyValuePair<K, V> pair in this.buckets[bucketIndex])
        {
            if (pair.Key.Equals(key))
            {
                return true;
            }
        }

        return false;
    }

    private int GetBucketIndex(K key)
    {
        int hashCode = key.GetHashCode();
        int bucketIndex = hashCode % this.buckets.Length;
        return bucketIndex;
    }

    private void Resize()
    {
        int newCapacity = this.buckets.Length * 2;
        var newBuckets = new LinkedList<KeyValuePair<K, V>>[newCapacity];

        foreach (LinkedList<KeyValuePair<K, V>> bucket in this.buckets)
        {
            if (bucket != null)
            {
                foreach (KeyValuePair<K, V> pair in bucket)
                {
                    int bucketIndex = GetBucketIndex(pair.Key);
                    if (newBuckets[bucketIndex] == null)
                    {
                        newBuckets[bucketIndex] = new LinkedList<KeyValuePair<K, V>>();
                    }

                    newBuckets[bucketIndex].AddLast(pair);
                }
            }
        }

        this.buckets = newBuckets;
    }
}
2790 chars
109 lines

In this implementation, the LinkedList class is used to implement the linked list and binary tree for each bucket, and the KeyValuePair struct is used to store key-value pairs. The GetHashCode() method of the key is used as the hash code to determine the bucket index. Resizing is triggered when the load factor exceeds 0.75, and the internal array is doubled in size.

gistlibby LogSnag