Is there a way to derive IEqualityComparer from IComparer? - c#

TL;DR I'm looking for a way to obtain IEqualityComparer<T> from IComparer<T>, no matter which datatype is T, including case-insensitive options if T is string. Or I need a different solution for this problem.
Here's full story: I'm implementing simple, generic cache with LFU policy. Requirement is that it must be possible to select whether the cache will be case sensitive or case insensitive -- if string happens to be the datatype for cache keys (which is not necessary). In the solution I primarily develop the cache for, I expect hundreds of billions of cache lookups, and cache sizes of max 100.000 entries. Because of that numbers I immediately resigned from using any string manipulation that causes allocations (such as .ToLower().GetHashCode() etc.) and instead opted to use IComparer and IEqualityComparer, as they are standard BCL features. User of this cache can pass the comparers to constructor. Here are relevant fragments of the code:
public class LFUCache<TKey,TValue>
{
private readonly Dictionary<TKey,CacheItem> entries;
private readonly SortedSet<CacheItem> lfuList;
private class CacheItem
{
public TKey Key;
public TValue Value;
public int UseCount;
}
private class CacheItemComparer : IComparer<CacheItem>
{
private readonly IComparer<TKey> cacheKeyComparer;
public CacheItemComparer(IComparer<TKey> cacheKeyComparer)
{
this.cacheKeyComparer = cacheKeyComparer;
if (cacheKeyComparer == null)
this.cacheKeyComparer = Comparer<TKey>.Default;
}
public int Compare(CacheItem x, CacheItem y)
{
int UseCount = x.UseCount - y.UseCount;
if (UseCount != 0) return UseCount;
return cacheKeyComparer.Compare(x.Key, y.Key);
}
}
public LFUCache(int capacity, IEqualityComparer<TKey> keyEqualityComparer,
IComparer<TKey> keyComparer) // <- here's my problem
{
// ...
entries = new Dictionary<TKey, CacheItem>(keyEqualityComparer);
lfuList = new SortedSet<CacheItem>(new CacheItemComparer(keyComparer));
}
// ...
}
The keyEqualityComparer is used to manage cache entries (so e.g. the key "ABC" and "abc" are equal if user wants to). The keyComparer is used to manage cache entries sorted by UseCount so that it's easy to select the least frequently used one (implemented in CacheItemComparer class).
Example correct usage with custom comparison:
var cache = new LFUCache<string, int>(10000,
StringComparer.InvariantCultureIgnoreCase,
StringComparer.InvariantCultureIgnoreCase);
(That looks stupid, but StringComparer implements both IComparer<string> and IEqualityComparer<string>.) The problem is that if user gives incompatible comparers (i.e. case insensitive keyEqualityComparer and case sensitive keyComparer), then the most likely outcome is invalid LFU statistics, and thus lower cache hits at best. The other scenario is also less than desired. Also if the key is more sophisticated (I'll have something resembling Tuple<string,DateTime,DateTime>), it's possible to mess it up more severely.
That's why I'd like to only have a single comparer argument in constructor, but that doesn't seem to work. I'm able to create IEqualityComparer<T>.Equals() with help of IComparer<T>.Compare(), but I'm stuck at IEqualityComparer<T>.GetHashCode() -- which is very important, as you know. If I had got access to private properties of the comparer to check if it's case sensitive or not, I would have used CompareInfo to get hash code.
I like this approach with 2 different data structures, because it gives me acceptable performance and controllable memory consumption -- on my laptop around 500.000 cache additions/sec with cache size 10.000 elements. Dictionary<TKey,TValue> is just used to find data in O(1), and SortedSet<CacheItem> inserts data in O(log n), find element to remove by calling lfuList.Min in O(log n), and find the entry to increment use count also in O(log n).
Any suggestions on how to solve this are welcome. I'll appreciate any ideas, including different designs.

It's not possible to implement an IComparer from an IEqualityComparer as you have no way of knowing whether an unequal item is greater than or less than the other item.
It's not possible to implement an IEqualityComparer from an IComparer as there's no way for you to generate a hash code that is in line with the IComparer's identity.
That said, there's no need for you to have both types of comparers in your case. When computing LRU you're comparing the time since an item was used as the primary comparer and then comparing based on a passed in comparer as a tiebreaker. Just remove that last part; don't have a tiebreaker. Let it be undefined which item leaves the cache when there is a tie for the least recently used. When you do that you only need to accept an IEqualityComparer, not an IComparer.

As I alluded to in my comment, you could add a helper method that might make things a little simpler for a basic use case:
public class LFUCache<TKey,TValue>
{
public static LFUCache<TKey, TValue> Create<TComp>(int capacity, TComp comparer) where TComp : IEqualityComparer<TKey>, IComparer<TKey>
{
return new LFUCache<TKey, TValue>(capacity, comparer, comparer);
}
}
and you'd use it like this:
var cache = LFUCache<string, int>.Create(10000, StringComparer.InvariantCultureIgnoreCase);

Okay next try. Here is an implementation for Add and Touch for LFU:
public class LfuCache<TKey, TValue>
{
private readonly Dictionary<TKey, LfuItem> _items;
private readonly int _limit;
private LfuItem _first, _last;
public LfuCache(int limit, IEqualityComparer<TKey> keyComparer = null)
{
this._limit = limit;
this._items = new Dictionary<TKey,LfuItem>(keyComparer);
}
public void Add(TKey key, TValue value)
{
if (this._items.Count == this._limit)
{
this.RemoveLast();
}
var lfuItem = new LfuItem { Key = key, Value = value, Prev = this._last };
this._items.Add(key, lfuItem);
if (this._last != null)
{
this._last.Next = lfuItem;
lfuItem.Prev = this._last;
}
this._last = lfuItem;
if (this._first == null)
{
this._first = lfuItem;
}
}
public TValue this[TKey key]
{
get
{
var lfuItem = this._items[key];
++lfuItem.UseCount;
this.TryMoveUp(lfuItem);
return lfuItem.Value;
}
}
private void TryMoveUp(LfuItem lfuItem)
{
if (lfuItem.Prev == null || lfuItem.Prev.UseCount >= lfuItem.UseCount) // maybe > if you want LRU and LFU
{
return;
}
var prev = lfuItem.Prev;
prev.Next = lfuItem.Next;
lfuItem.Prev = prev.Prev;
prev.Prev = lfuItem;
if (lfuItem.Prev == null)
{
this._first = lfuItem;
}
}
private void RemoveLast()
{
if (this._items.Remove(this._last.Key))
{
this._last = this._last.Prev;
if (this._last != null)
{
this._last.Next = null;
}
}
}
private class LfuItem
{
public TKey Key { get; set; }
public TValue Value { get; set; }
public long UseCount { get; set; }
public LfuItem Prev { get; set; }
public LfuItem Next { get; set; }
}
}
In my opinion it looks like that Add and Touch is in O(1), isn't it?
Currently I don't see any use case for _first but maybe anyone else need it. To remove an item _last should be enough.
EDIT
A single linked list will also do if you don't need a MoveDown operation.
EDIT No a single linked list will not work because MoveUp need the Next pointer to change it's Prev pointer.

Instead of taking an IEqualityComparer and an IComparer in your constructor, you could try taking an IComparer and a lambda which defines GetHashCode(). Then build an IEqualityComparer based on if(IComparer==0) and GetHashCode() = lambda.
Although I would say it is small, you still have the risk of getting HashCode mismatches when IComparer returns 0. If you want to make it super clear to the user of your code, you could always extend the strategy by taking two lambdas in the constructor: Func<T,T,int> used for both IComparer and IEqualityComparer, and Func<T,int> for GetHashCode.

Related

Use HashSet as Dictionary Key - Compare all elements

I am checking if a total group of edges already contains the connection between 2 points.
I want to use HashSet's that will contain 2 vectors as Dictionary keys. Then I want to be able to call a performant Dictionary.ContainsKey(hashSet). I want the contains/equality check to be dependent on the Vectors in the Set.
Fex. If I add HashSet [V000 V001] to the Dict. I want to get Dictionary.ContainsKey(HashSet [V001 V000]) return true. (HashSet, so the order can vary, just the same Elements)
The Problem seems to be, that the Dictionary.ContainsKey() method does see separately created HashSets as different objects, even though, they contain the same elements.
Dictionary<HashSet<Vector3>, Vector3> d = new Dictionary<HashSet<Vector3>, Vector3>();
HashSet<Vector3> s = new HashSet<Vector3>();
s.Add(Vector3.one);
s.Add(Vector3.zero);
d.Add(s);
HashSet<Vector3> s2 = new HashSet<Vector3>();
s2.Add(Vector3.zero);
s2.Add(Vector3.one);
bool doesContain = d.ContainsKey(s2); // should be true
You also may suggest a better way of doing this 'Contains()' check efficiently.
The HashSet type doesn't do the equality comparison you want out of the box. It only has reference equality.
To get what you want, you'll need a new type to use as the Dictionary key. The new type will have a HashSet property, and overload Equals() and GetHashCode(), and may as well implement IEquatable at this point as well.
I'll get you started:
public class HashKey<T> : IEquatable<HashKey<T>>
{
private HashSet<T> _items;
public HashSet<T> Items
{
get {return _items;}
private set {_items = value;}
}
public HashKey()
{
_items = new HashSet<T>();
}
public HashKey(HashSet<T> initialSet)
{
_items = initialSet ?? new HashSet();
}
public override int GetHashCode()
{
// I'm leaving this for you to do
}
public override bool Equals(Object obj)
{
if (! (obj is HashKey)) return false;
return this.GetHashCode().Equals(obj.GetHashCode());
}
public bool Equals(HashSet<T> obj)
{
if (obj is null) return false;
return this.GetHashCode().Equals(obj.GetHashCode());
}
}
You want to use a hashset as key.
So the keys are references where one key is one hashset reference.
The ContainsKey compare references.
For what you want to do, you can create a class that implements IEqualityComparer to pass it to the dictionary constructor.
https://learn.microsoft.com/dotnet/api/system.collections.generic.iequalitycomparer-1
If you want a full management, you should create a new class embedding the dictionary and implement your own public operations wrapping that of the dictionary : ContainsKey and all others methods you need.
public class MyDictionary : IEnumerable<>
{
private Dictionary<HashSet<Vector3>, Vector3> d
= new Dictionary<HashSet<Vector3>, Vector3>();
public int Count { get; }
public this...
public ContainsKey()
{
// implements your own comparison algorithm
}
public Add();
public Remove();
...
}
So you will have a strongly typed dictionary for your intended usage.

Keep the most recent KeyValuePairs in a SortedList

I have a SortedList that adds KeyValuePairs every 10 min. I'm trying to keep the most recent 10 KeyValuePairs and remove all prior pairs but what I'm doing isn't working. Below I attached my code with explanation along each step. Any help is greatly appreciated.
private SortedList<int, double> myList = new SortedList<int, double>();
// Every 10 minutes a new KeyValuePair is added to myList so
// I have no issue with sorting. I'm only trying to get the most
// recent 10 KeyValuePairs.
// My Attempt (the only one that worked without errors)
int mylistCount = 10;
if (myList.Count()>mylistCount)
{myList.Clear();}
// The issue with my attempt is that it erases the entire myList
// As a result, whenever myList reaches 10, it goes back to Zero.
// What I'm trying to do is keep myList Count at 10 containing only
// the most recent KeyValuePairs.
**
In myList, the Key int is PlayerID# (which is random) and the Value is that Player's Score %
To answer your questions:
Sorting is not an issue with the current set up.
It does not have to be a SortedList, I'm open to any suggestion. I'm just more familiar with using Dictionaries and Lists.
I have never used a Queue but open to trying it. (Will have to research that, I'm learning something new everyday)
There are no time stamps and the timing of new entries is not important. All I'm trying to do is make sure that myList only has the most recent 10.
What I'm trying to do is keep myList Count at 10 containing only the
most recent KeyValuePairs.
You're wanting to keep the 10 most recent pairs so I assume the sorting is by addition time. If that's true, you don't need to have them sorted and thus do not need a SortedList. You could use a Queue as suggested in a comment.
A queue is first in, first out (FIFO). That means you know the first element in the queue is the oldest and the one you need to dequeue when the eleventh element comes in. For example, couldn't this do the trick with little ceremony?
// q is a Queue (FIFO)
if (q.Count == 10)
{
// we've reached our cap, remove the element at the
// front of the q (the oldest one)
q.Dequeue();
}
// we'll always add the newest element to the end of the q
q.Enqueue(new KeyValuePair<int, double>(key, value));
How about using a LinkedList instead of a SortedList.
if(myLinkedList.Count() > 10)
myLinkedList.RemoveFirst();
This will always remove the first added item of the list.
Without knowing much about the key, I offer a simple solution:
Create a class to represent the value as well as the time it was added and implements the IComparable<T> interface:
public class TimeStampedDouble : IComparable<TimeStampedDouble>
{
public TimeStampedDouble(double value)
{
Value = value;
Date = DateTime.Now;
}
public double Value { get; private set; }
public DateTime Date { get; private set; }
public int CompareTo(TimeStampedDouble other)
{
return this.Date.CompareTo(other.Date);
}
// User-defined conversion to double, for convenience
public static implicit operator double(TimeStampedDouble d)
{
return d.Value;
}
}
Change your list to store this type instead:
SortedList<int, TimeStampedDouble> list = new SortedList<int, TimeStampedDouble>();
Add items to the list using the new class:
//In this line, 1 is the key, 6 is the double you are storing.
myList.Add(1, new TimeStampedDouble(6));
myList.Add(3, new TimeStampedDouble(5));
myList.Add(2, new TimeStampedDouble(4));
myList.Add(7, new TimeStampedDouble(3));
myList.Add(5, new TimeStampedDouble(2));
You can now get the oldest item using Linq and remove it:
if (myList.Count() > mylistCount)
{
var oldest = myList.OrderByDescending(i => i.Value).FirstOrDefault();
myList.Remove(oldest.Key);
}
Item with key 5 is removed.
It is not necessary to check if oldest is null because a) it's a value type and b) a check is made for a minimum number of items so the assumption is that the list will always have at least one item, provided mylistCount is greater than 0.
Because an implicit conversion to double is provided, you can use the value without explicit casting:
double doubleValue = myList[7];
I think that the most convenient solution would be to use a bounded list, to ensure that the elements in the list will never exceed the maximum count. Implementing such a list is not very difficult. Probably the most flexible way is to implement the IDictionary<TKey, TValue> interface, delegating the work to an internal SortedList<TKey, TValue>. Bellow is an inheritance-based approach, that requires less code. Every time an added element causes the Count to become larger than the boundedCapacity, the oldest element in the list is automatically removed.
public class BoundedSortedList<TKey, TValue> : SortedList<TKey, TValue>
{
private readonly int _boundedCapacity;
private readonly List<TKey> _queue = new List<TKey>();
public BoundedSortedList(int boundedCapacity)
{
_boundedCapacity = boundedCapacity;
}
public new void Add(TKey key, TValue value)
{
base.Add(key, value);
_queue.Add(key);
if (this.Count > _boundedCapacity)
{
var keyToRemove = _queue[0];
this.Remove(keyToRemove);
}
}
public new TValue this[TKey key]
{
get { return base[key]; }
set { this.Remove(key); this.Add(key, value); }
}
public new bool Remove(TKey key) { _queue.Remove(key); return base.Remove(key); }
public new bool RemoveAt(int index) => throw new NotImplementedException();
public new void Clear() { base.Clear(); _queue.Clear(); }
}
Usage example:
var myList = new BoundedSortedList<int, double>(10);
Incorrect usage example:
var myIList = (IDictionary<int, double>)myList;
This will not work because accessing the class through the interface will bypass the logic that makes the list bounded.
Here is what worked for me:
if (myList.Count()>mylistCount)
{myList.Remove(myList.FirstOrDefault());}
Thank you all

How to get dictionary item based on an object with the same hashcode?

Consider the following private member:
private ConcurrentDictionary<CollectionInfo, ServiceInfo> _collectionsServicesMapping;
The class CollectionInfo overrides and adds some additional properties:
class CollectionInfo
{
public Guid InstanceId { get; set; }
public string CollectionName { get; set; }
public string WorkFlowName { get; set; }
public Guid DomainId { get; set; }
public override bool Equals(object obj)
{
return obj is CollectionInfo && (obj as CollectionInfo).InstanceId.Equals(InstanceId);
}
public override int GetHashCode()
{
return InstanceId.GetHashCode();
}
}
In the context that I need, I'm looking for CollectionInfo by InstanceId:
private IRequestHandler GetServiceByInstanceId(Guid instanceId)
{
}
Two optional solutions that I see:
_collectionsServicesMapping.TryGetValue(new CollectionInfo() { InstanceId = instanceId }, out si)
_collectionsServicesMapping.FirstOrDefault(x => x.Key.InstanceId.Equals(instanceId));
But this force me either create a redundant fake instance of the class CollectionInfo or to scan all the dictionary.
Is there a way to get a dictionary item, based on an object that has the same hashcode in another more efficient way?
Is there a way to get a dictionary item, based on an object that has the same hashcode in another more efficient way?
Unfortunately no. Contrary to the Philip Pittle's answer, I think you (and anyone else in the similar situation) do have a problem. We are victims of what I would call too much encapsulation, started with Dictionary<TKey, TValue> and followed by the ConcurentDictionary<TKey, TValue>. Both classes could easily expose a method like
IEnumerable<KeyValuePair<TKey, TValue>> GetItems(int hashCode)
or
bool TryGetValue(int hashCode, Func<TKey, bool> predicate, out TValue value)
but they don't. Unfortunately something similar cannot be simulated outside the class implementation.
So you are stuck with the workarounds mentioned. I would pick the fake instance approach - at least you can, sometimes there is no such luxury (if the class requires complex constructor with strong validation not allowing fake instantiation). And wait for MS to open source the BCL :-)
P.S. What about creating a different dictionary by Guid, why should one keep 2 copies of a Guid (16 bytes value type) if it's already contained in a instance of a class?
I don't think you really have a problem here. But let's walk through it (see the end for what I'd recommend).
Create a redundant fake instance
Creating a new instance is a relatively inexpensive operation. Is the code a bit uglier then using the Guid directly, sure. But if that's your concern, you have plenty of options:
//excention method (in some static class)
public static ServiceInfo GetServiceByGuid (
this ConcurrentDictionary<CollectionInfo, ServiceInfo> dic, Guid id){
ServiceInfo si;
dic.TryGetValue(new CollectionInfo() { InstanceId = id}, out si);
return si;
}
or
//implicit coversion operator (in CollectionInfo)
public static implicit operator CollectionInfo(Guid id){
return new CollectionInfo(new CollectionInfo() { InstanceId = id};
}
then you can just pass the Guid instanceId to the dictionary's TryGetValue method.
Scan all the dictionary
There's no reason to take this approach. You're technically scanning only the keys and not the "entire" dictionary, but TryGetValue will be more performant as it can leverage hashing to quickly find the Item you're looking for.
Change the dictionary
So after that, this is what I think you want:
ConcurrentDictionary<Guid, Tuple<CollectionInfo, ServiceInfo>>
This way you still get concurrency, you can match CollectionInfo/ServiceInfo based on a guid (id), and you don't have to mess with overloads (GetHashCode() in CollectionInfo).
private IRequestHandler GetServiceByInstanceId(Guid instanceId)
{
Tuple<CollectionInfo,ServiceInfo> pair;
if (_collectionsServicesMapping.TryGetValue(instanceId, out pair))
{
return pair.Item2;
}
// whatever you want to return if instanceId wasn't found
return null;
}

Alternative to increasingly slow Dictionary.Add(Key,Value)?

By "increasingly" what I mean is that Add is fast at the beginning when there is a low number of keys. After inserting 20% of the keys, it gets very slow. After 50% it gets unbearably slow.
I get that the lower the number of keys, the faster the "key collision search" when adding new elements to the dictionary. But is there any possible way to skip this downside while keeping the Dictionary? I know beforehand that keys don't collide so no check is needed, but I don't know if there is any way to successfully use this info in the code.
BTW I am forced to use the dictionary structure because of architecture restrictions (this structure is swallowed later by a db exporter).
What my code does:
var keyList = GetKeyList();
var resultDict = new Dictionary<T,T>();
foreach (var key in keyList)
{
resultDict.Add(key,someResult);
}
Edit: since people is asking how the hash code is generated, I will try to clarify this.
Theoretically I have no control over the hash code generation, because unfortunately it uses a convention between multiple systems that are connected through the same db.
In practice, the piece of code that generates the hash code is indeed my code (disclaimer: it wasn't me choosing the convention that is used in the generation).
The key generation is way more complicated than that, but it all boils down to this:
private List<ResultKey> GetKeyList(string prefix, List<float> xCoordList, List<float> yCoordList)
{
var keyList = new List<ResultKey>();
var constantSensorName = "xxx";
foreach (float xCoord in xCoordList)
{
foreach (float yCoord in yCoordList)
{
string stationName = string.Format("{0}_E{1}N{2}", prefix, xCoord, yCoord);
keyList.Add(new ResultKey(constantSensorName, stationName));
}
}
return keyList;
}
public struct ResultKey
{
public string SensorName { get; set; }
public string StationName { get; set; }
public ResultKey(string sensorName, string stationName)
{
this.SensorName = sensorName;
this.StationName = stationName;
}
}
The first thing that comes to mind is to create your own hashing function. The Add method for the dictionary is going to call the default implementation of the getHashCode() method when it goes to add it to the structure. If you put a wrapper class around your keys and overwrote the getHashCode() method, then you could write your own hashing function which, presumably, could implement a less collision prone hash function.
You are using the default hash code generation for your struct ResultKey. The default hash code generation for structs is disappointingly bad. You can't rely on that here because your struct contains two strings which trigger a bad case (see the linked answer). Essentially, only your SensorName field makes it into the hash code, nothing else. That causes all keys with the same SensorName to collide.
Write your own function. I quickly generated one using Resharper:
public struct ResultKey : IEquatable<ResultKey>
{
public string SensorName { get; set; }
public string StationName { get; set; }
public ResultKey(string sensorName, string stationName)
{
this.SensorName = sensorName;
this.StationName = stationName;
}
public bool Equals(ResultKey other)
{
return string.Equals(SensorName, other.SensorName) && string.Equals(StationName, other.StationName);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is ResultKey && Equals((ResultKey)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((SensorName != null ? SensorName.GetHashCode() : 0)*397) ^ (StationName != null ? StationName.GetHashCode() : 0);
}
}
public static bool operator ==(ResultKey left, ResultKey right)
{
return left.Equals(right);
}
public static bool operator !=(ResultKey left, ResultKey right)
{
return !left.Equals(right);
}
}
Your ResultKey contains two strings, so you need a hashcode that combine them.
"How do I calculate a good hash code for a list of strings?" contains some answer showing how to do this.
However you get do a lot worse then
public override int GetHashCode()
{
return (SensorName + StationName).GetHashCode();
}
If you just want to fulfill API requirements and need a dirty solution, you could implement your own Dictionary.
public class FakeFastDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
protected IList<KeyValuePair<TKey, TValue>> _list
= new List<KeyValuePair<TKey, TValue>>();
public new void Add(TKey key, TValue value)
{
_list.Add(new KeyValuePair<TKey, TValue>(key, value));
}
public new ICollection<TValue> Values
{
get
{
// there may be faster ways to to it:
return _list.Select(x => x.Value).ToArray();
}
}
public new ICollection<TKey> Keys
{
get
{
// there may be faster ways to to it:
return _list.Select(x => x.Key).ToArray();
}
}
}
This is a running sample:
https://dotnetfiddle.net/BDyks0

Is there an IDictionary implementation for large dictionaries not to end up on large object heap?

This is closely related to .NET Collections and the Large Object Heap (LOH). In a nutshell, if there are more than 85K buckets, it's automatically on LOH and when it's released is unknown. Does anyone aware of a good implementation of IDictionary based on lists of array or something like it that prevents it from going to LOH?
You can use the SortedDictionary, which is a binary tree.
If you need the Dictionary's O(1) performance, or something closer to that, you could use a different hash table implementation that stores the data in chunks small enough not to go on the LOH. I'm not aware of anything publicly available; I have used SortedDictionary in the past and found that the decrease in performance was minimal, so I didn't look any further.
Here's a start of one option. I assume you can follow the pattern given to implement the other methods.
Just change the numDictionaries to determine how it's broken up.
If you really need to you could make the number of dictionaries dynamic and have it add more when the existing ones get sufficiently large.
public class NonContigousDictionary<TKey, TValue>
//TODO make this implement IEnumerable, IDictionary,
//and any other relevant interfaces.
{
public Dictionary<TKey, TValue>[] dictionaries;
private readonly int numDictionaries = 5;
public NonContigousDictionary()
{
dictionaries = Enumerable.Range(0, numDictionaries)
.Select(_ => new Dictionary<TKey, TValue>())
.ToArray();
}
public TValue this[TKey key]
{
get
{
int hash = key.GetHashCode();
return dictionaries[GetBucket(hash)][key];
}
set
{
int hash = key.GetHashCode();
dictionaries[GetBucket(hash][key] = value;
}
}
public bool Remove(TKey key)
{
int hash = key.GetHashCode();
return dictionaries[GetBucket(hash].Remove(key);
}
public void Clear()
{
foreach (var dic in dictionaries)
{
dic.Clear();
}
}
private int GetBucket(int hash)
{
return (hash % numDictionaries + numDictionaries) % numDictionaries;
}
}

Categories

Resources