Care to review my first hash map? This is my first attempt at a hash map and I feel it is a great start. Although it does have a certain naive feel that I'm having trouble with.

public class HashMap&lt;TKey, TValue&gt;
{
    const int BlockSize = 100000;
    const int NumberOfBlocks = 42950;

    readonly Entry[][] _blocks = new Entry[NumberOfBlocks][];
    readonly IEqualityComparer&lt;TKey&gt; _comparer = EqualityComparer&lt;TKey&gt;.Default;


    public void Add(TKey key, TValue value)
    {           
        uint hashCode = (uint)_comparer.GetHashCode(key);
        int blockIndex = (int)(hashCode / BlockSize);
        int slotIndex = (int)(hashCode - blockIndex * BlockSize);

        var block = _blocks[blockIndex];
        if (block == null)
        {
            block = _blocks[blockIndex] = new Entry[BlockSize];
        }

        var entry =  block[slotIndex];
        if (entry == null)
        {
            entry = block[slotIndex] = new Entry(key, value);
        }
        else
        {
            while (entry != null &amp;&amp; entry.Next != null)
            {
                if (_comparer.Equals(key, entry.Key))
                {
                    throw new Exception();
                }
                entry = entry.Next;
            }
            entry = entry.Next = new Entry(key, value);
        }
    }

    public bool ContainsKey(TKey key)
    {
        uint hashCode = (uint)_comparer.GetHashCode(key);
        int blockIndex = (int)(hashCode / BlockSize);
        int slotIndex = (int)(hashCode - blockIndex * BlockSize);

        var block = _blocks[blockIndex];
        if (block == null)
            return false;

        var entry = block[slotIndex];
        while (entry != null)
        {
            if (_comparer.Equals(key, entry.Key))
                return true;
            entry = entry.Next;
        }
        return false;
    }

    class Entry
    {
        public readonly TKey Key;
        public readonly TValue Value;
        public Entry Next;

        public Entry(TKey key, TValue value)
        {
            Key = key;
            Value = value;
        }
    }
}
(I'm assuming you're implementing your own hashtable as an exercise/kata instead of using the CLR's, as I haven't seen anything unique here that justifies rolling your own.)

Here's some feedback:


HashMap should implement core interfaces and functionality as other .NET containers do (e.g.: IEnumerable&lt;T&gt;, IDictionary&lt;TKey,TValue&gt;, provide an enumerator implementation). As a guide, look at the Dictionary&lt;TKey,TValue&gt; class and see what interfaces it implements. I'd expect your code to have a similar level of service;
_blocks should be a List instead of an array, and its capacity (NumberOfBlocks) should be passed optionally through an optional constructor argument. Same applies to the equality comparer. Notice that these initial values seem very, very high - allocating a 42950-Entry[] array right off the bet for an empty container, and an additional 100,000 Entry array at first addition;
Why cast hashCode to uint?
Notice the duplicate code between Add and ContainsKey. This should be cleanly refactored;
blockIndex's and slotIndex's calculations do not seem correct. For example, you are dividing by 100,000, so this will work because int.MaxValue &lt; 100000 * 100000. However, if you make this configurable and a lower value is used, you could go past your _blocks size. You should use modulo instead of division. Also, slotIndex will always be zero (or 1 due to rounding) as (hashCode - blockIndex * BlockSize) == (hashCode - hashCode/BlockSize * BlockSize) == (hashCode - hashCode) == 0. NumberOfBlocks is not used at all here, which is weird;
You should use LinkedList instead of rolling your own linked list (again, I'm assuming you're writing some exercise code, otherwise it's a must). Or at least you should define properties instead of exposing the values as public members;
Don't throw Exception. Create your own custom exception with a proper name;
Setting the entry variable in the if statement in Add is needless and just reduces readability.
The while loop in Add is a bit weird. Why do you need to check entry and entry.Next? I think you meant entry.Next only.
Setting entry at the end of the while look is needless and just reduces readability. I'd actually rename entry to previous for clarity.


Did you write any unit tests for this? Make sure you do so.

EDIT: typo: CRL => CLR