Skip to content

Instantly share code, notes, and snippets.

@aannenko
Last active December 17, 2024 09:05
Show Gist options
  • Save aannenko/0afb2c12a68e2399ad3febe63bb05f17 to your computer and use it in GitHub Desktop.
Save aannenko/0afb2c12a68e2399ad3febe63bb05f17 to your computer and use it in GitHub Desktop.
A Dictionary class that uses an array of linked lists to hold the key-value pairs
var myDict = new NaiveDictionary<int, string?>();
myDict[1] = "abc";
myDict[1] = "def";
myDict[2] = "def";
myDict[3] = "def";
myDict[4] = "def";
myDict[5] = "def";
myDict[6] = "def";
myDict[7] = "def";
myDict[8] = "def";
myDict[9] = "def";
myDict[10] = "def";
myDict[11] = "def";
myDict[12] = "def";
myDict[13] = "def";
myDict[14] = "def";
myDict[15] = "def";
myDict[16] = "def";
myDict[17] = "def";
myDict[18] = "def";
myDict[1].Dump();
myDict[2].Dump();
myDict[3].Dump();
myDict[4].Dump();
myDict[5].Dump();
myDict[6].Dump();
myDict[7].Dump();
myDict[8].Dump();
myDict[18].Dump();
myDict[19].Dump();
sealed class NaiveDictionary<TKey, TValue> where TKey : notnull, IEquatable<TKey>
{
private LinkedList<(TKey Key, TValue? Value)>[] _buckets = new LinkedList<(TKey, TValue?)>[4];
private int _count = 0;
public TValue? this[TKey key]
{
get
{
if (TryGetValue(key, out var val))
return val;
throw new ArgumentException("Key does not exist");
}
set
{
SetInternal(key, value, true);
}
}
public bool TryGetValue(TKey key, out TValue? value)
{
ArgumentNullException.ThrowIfNull(key);
var bucket = _buckets[key.GetHashCode() % _buckets.Length];
if (bucket is not null)
{
foreach (var (k, v) in bucket)
{
if (key.Equals(k))
{
value = v;
return true;
}
}
}
value = default;
return false;
}
public bool Add(TKey key, TValue? value) =>
SetInternal(key, value, true);
public bool TryAdd(TKey key, TValue? value) =>
SetInternal(key, value, false);
private bool SetInternal(TKey key, TValue? value, bool replaceExisting = false)
{
ArgumentNullException.ThrowIfNull(key);
GrowBucketsIfNeeded();
var bucketIndex = key.GetHashCode() % _buckets.Length;
var bucket = _buckets[bucketIndex] ?? (_buckets[bucketIndex] = new());
for (var node = bucket.First; node != null; node = node.Next)
{
if (node.Value.Key.Equals(key))
{
if (replaceExisting)
{
node.Value = new(key, value);
return true;
}
else
{
return false;
}
}
}
bucket.AddLast((key, value));
_count++;
return true;
}
private void GrowBucketsIfNeeded()
{
if (_count * 0.75 < _buckets.Length)
return;
_count = 0;
var oldBuckets = _buckets;
var newBuckets = new LinkedList<(TKey Key, TValue? Value)>[oldBuckets.Length * 2];
foreach (var bucket in oldBuckets)
{
if (bucket is null || bucket.Count == 0)
continue;
foreach (var (key, value) in bucket)
{
var newBucketIndex = key.GetHashCode() % newBuckets.Length;
LinkedList<(TKey, TValue?)> newBucket;
if (newBuckets[newBucketIndex] is null)
{
_count++;
newBucket = newBuckets[newBucketIndex] = new();
}
else
{
newBucket = newBuckets[newBucketIndex];
}
newBucket.AddLast((key, value));
}
}
_buckets = newBuckets;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment