How to achieve remove_if functionality in .NET ConcurrentDictionary - c#

I have a scenario where I have to keep reference counted object for given key in the ConcurrentDictionary, if reference count reaches 0, I want to delete the key. This has to be thread safe hence I am planning to use the ConcurrentDictionary.
Sample program as follows. In the concurrent dictionary, I have key and value , the value is KeyValuePair which holds my custom object and reference count.
ConcurrentDictionary<string, KeyValuePair<object, int>> ccd =
new ConcurrentDictionary<string, KeyValuePair<object, int>>();
// following code adds the key, if not exists with reference
// count for my custom object to 1
// if the key already exists it increments the reference count
var addOrUpdateValue = ccd.AddOrUpdate("mykey",
new KeyValuePair<object, int>(new object(), 1),
(k, pair) => new KeyValuePair<object, int>(pair.Key, pair.Value + 1));
Now I want a way to remove the key when the reference count reaches to 0. I was thinking , remove method on ConcurrentDictionary which takes key and predicate , removes the key if the predicate return 'true'. Example.
ConcurrentDictionary.remove(TKey, Predicate<TValue> ).
There is no such method on ConcurrentDictionary, question is how to do the same in thread safe way ?.

.NET doesn't expose a RemoveIf directly, but it does expose the building blocks necessary to make it work without doing your own locking.
ConcurrentDictionary implements ICollection<T>, which has a Remove that takes and tests for a full KeyValuePair instead of just a key. Despite being hidden, this Remove is still thread-safe and we'll use it to implement this. One caveat for this to work is that Remove uses EqualityComparer<T>.Default to test the value, so it must be equality comparable. Your current one is not, so we'll re-implement that as such:
struct ObjectCount : IEquatable<ObjectCount>
{
public object Object { get; }
public int Count { get; }
public ObjectCount(object o, int c)
{
Object = o;
Count = c;
}
public bool Equals(ObjectCount o) =>
object.Equals(Object, o.Object) && Count == o.Count;
public override bool Equals(object o) =>
(o as ObjectCount?)?.Equals(this) == true;
// this hash combining will work but you can do better.
// it is not actually used by any of this code.
public override int GetHashCode() =>
(Object?.GetHashCode() ?? 0) ^ Count.GetHashCode();
}
And finally, we'll define a method to increment/decrement counts from your dictionary:
void UpdateCounts(ConcurrentDictionary<string, ObjectCount> dict, string key, int toAdd)
{
var addOrUpdateValue = dict.AddOrUpdate(key,
new ObjectCount(new object(), 1),
(k, pair) => new ObjectCount(pair.Key, pair.Value + toAdd));
if(addOrUpdateValue.Count == 0)
{
((ICollection<KeyValuePair<string, ObjectCount>>)dict).Remove(
new KeyValuePair<string, ObjectCount>(key, addOrUpdateValue));
}
}
The value for that key might be changed between the calls of AddOrUpdate and Remove, but that doesn't matter to us: because Remove tests the full KeyValuePair, it will only remove it if the value hasn't changed since the update.
This is the common lock-free pattern of setting up a change and then using a final thread-safe op to safely "commit" the change only if our data structure hasn't been updated in the meantime.

You can't use a ConcurrentDictionary because it does not expose its internal locking. Your increment must occur under the same lock that controls the add (a simple interlocked add is not enough as a concurrent thread may remove the object before you increment the count). Similarly, the decrement must acquire the lock to be able to safely remove it if it reaches 0 count. What this spell is that you must use a dictionary for which you control the locking explicitly.

I had a similar issue - having multi-threaded piece of code, I needed to count the number of times I've accessed certain type of resource. In other words - I need to find distribution of access to different resource types.
The way I've solved it:
Create store for your counts:
ConcurrentDictionary<string, StrongBox<int>> _counts = new ConcurrentDictionary<string, StrongBox<int>>();
When the resource is accessed, increment access count:
Interlocked.Increment(ref _counts.GetOrAdd(_resourceType, new StrongBox<int>(0)).Value);
In your case you'll have to take care of decrement as well.
I know it's not a full solution to the problem you've presented, and it's not a direct answer to it, but I hope it can be useful to someone.

Currently (.NET 6) the ConcurrentDictionary<K,V> class has no API available that allows to update or remove a key, based on a user-supplied delegate. This functionality can be achieved laboriously by using the methods TryGetValue, TryUpdate and TryRemove in a loop:
string key = "mykey";
while (true)
{
if (!ccd.TryGetValue(key, out KeyValuePair<object, int> existingValue))
break; // The key was not found
// Create new value based on the existing value
KeyValuePair<object, int> newValue = KeyValuePair
.Create(existingValue.Key, existingValue.Value - 1);
if (newValue.Value > 0)
{
if (ccd.TryUpdate(key, newValue, existingValue))
break; // The key was updated successfully
}
else
{
if (ccd.TryRemove(KeyValuePair.Create(key, existingValue)))
break; // The key was removed successfully
}
// We lost the race to either TryUpdate or TryRemove. Try again.
}
In case there is no contention, the loop will exit after a single iteration.
I have made a proposal on GitHub, for an API TryUpdateOrRemove that would fill this void. In case this proposal is accepted, the above code could be reduced to this:
ccd.TryUpdateOrRemove(key, (_, existingValue) =>
{
KeyValuePair<object, int> newValue = KeyValuePair
.Create(existingValue.Key, existingValue.Value - 1);
if (newValue.Value > 0) return (UpdateRemoveResult.Update, newValue);
return (UpdateRemoveResult.Remove, default);
});
In case you like this proposal, make sure to give it an upvote on GitHub. Not only it's less code, but it should be also more efficient because the key would be hashed only once.

This will give you a dictionary that tracks the count of an item if it is not zero and has no item when it is 0. The increment and decrement are fairly straightforward. The Remove empty node looks odd, but preserves accurate count even if adds and removes come in out of order. The decrement initial value of -1, again is to handle when calls come in out of order.
Concurrent programming is weird sometimes.
private void Increment(string key)
{
var result = ccd.AddOrUpdate(key,new KeyValuePair<object, int>(new object(), 1),(k, pair) => new KeyValuePair<object, int>(pair.Key, pair.Value + 1));
RemoveEmptyNode(key, result);
}
private void Decrement(string key)
{
var result = ccd.AddOrUpdate(key, new KeyValuePair<object, int>(new object(), -1), (k, pair) => new KeyValuePair<object, int>(pair.Key, pair.Value - 1));
RemoveEmptyNode(key, result);
}
private void RemoveEmptyNode(string key, KeyValuePair<object, int> result)
{
if (result.Value == 0)
{
KeyValuePair<object, int> removedKeyValuePair;
if (ccd.TryRemove(key, out removedKeyValuePair))
{
if (removedKeyValuePair.Value != 0)
{
ccd.AddOrUpdate(key, removedKeyValuePair,
(k, pair) => new KeyValuePair<object, int>(key, pair.Value + removedKeyValuePair.Value));
}
}
}
}
}

Related

How to add a new element to a hashset that is value of a ConcurrentDictionary?

I have a ConcurrentDictionary that has as key a long and as value a hashset of int. I want that if the key isn't in the dictionary, add a new hashset with the first element. If the key exists, add the new element to the existing dictionary.
I am trying something like that:
ConcurrentDictionary<long, HashSet<int>> myDic = new ConcurrentDictionary<long, HashSet<int>>();
int myElement = 1;
myDic.AddOrUpdate(1, new Hashset<int>(){myFirstElement},
(key, actualValue) => actualValue.Add(myElement));
The problem with this code is the third parameter, because .Add() method returns a bool and the AddOrUpdate expects a hashset. The first and second parameters are right.
So my question is how I can add a new element to the hashset in thread-safe way and avoid duplicates (it is the reason why I am using a hashset as value). The problem of the hashset is that it is not thread-safe and if I get it first and later add the new element, I am doing outside of the dictionary and I could have problems.
Thanks.
To fix compiler error you can do this:
myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement },
(key, actualValue) => {
actualValue.Add(myFirstElement);
return actualValue;
});
BUT this is not thread safe, because "update" function is not run inside any lock so you are potentially adding to not-thread-safe HashSet from multiple threads. This might result in (for example) losing values (so you were adding 1000 items to HashSet but in the end you have only 970 items in it for example). Update function in AddOrUpdate should not have any side effects and here it does.
You can lock yourself over adding values to HashSet:
myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement },
(key, actualValue) => {
lock (actualValue) {
actualValue.Add(myFirstElement);
return actualValue;
}
});
But then question is why you are using lock-free structure (ConcurrentDictionary) in the first place. Besides that - any other code might get HashSet from your dictionary and add value there without any locks, making the whole thing useless. So if you decide to go that way for some reason - you have to ensure that all code locks when accessing HashSet from that dictionary.
Instead of all that - just use concurrent collection instead of HashSet. There is no ConcurrentHashSet as far as I know but you can use another ConcurrentDictionary with dummy keys as a replacement (or look over internet for custom implementations).
Side note. Here
myDic.AddOrUpdate(1, new Hashset<int>(){myFirstElement},
you create new HashSet every time when calling AddOrUpdate, even if that dictionary is not needed because key is already there. Instead use overload with add value factory:
myDic.AddOrUpdate(1, (key) => new HashSet<int>() { myFirstElement },
Edit: sample usage of ConcurrentDictionary as hash set:
var myDic = new ConcurrentDictionary<long, ConcurrentDictionary<int, byte>>();
long key = 1;
int element = 1;
var hashSet = myDic.AddOrUpdate(key,
_ => new ConcurrentDictionary<int, byte>(new[] {new KeyValuePair<int, byte>(element, 0)}),
(_, oldValue) => {
oldValue.TryAdd(element, 0);
return oldValue;
});
If you wrap the anonymous function definition in curly braces, you can define multiple statements in the body of the function and thus specify the return value like this:
myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement },
(key, actualValue) => {
actualValue.Add(myElement);
return actualValue;
});

Sorted Dictionary sorted on value in C# (LRU cache)

I want to implement an LRU cache , where least recently used elements will be evicted asynchronously . My current idea is to use a Dictionary to store the <key,value> pairs , and to keep track of the times of accesses of the objects, to keep a SortedDictionary <key, timestamp>. The idea is for the async thread to get the LRU items from the SortedDictionary and remove from the cache . But for this to work, SortedDictionary needs to sort by value, which it does not.
I could have used a separate SortedList instead of the SortedDictionary for keeping the {key and timestamp} sorted on the timestamp , but then I'll have to do a "linear" lookup for finding the key from the list (when I have to UPDATE the timestamp, when the same key is accessed again) - I am looking for a better than linear way if possible. Can someone share ideas to deal with this problem?
So, my problem boils down to this:
I've to lookup keys in <= logn time for UPDATING the timestamp while at the same time able to get the keys sorted based on the timestamp .
One way thought of was to keep a SortedDictionary of <{key,timestamp},null> which orders the keys based on the timestamp part of {key,timestamp} . While this is fine , the problem is hashcode() will just have to return key.hashcode() (for lookup while updating timestamp) , while equals() should also use timestamp . So , equals() and hashcode() are in conflict , so felt that this is not a good idea ...
What you should do is keep two dictionaries, one sorted by time and one by keys.
Remember that dictionaries are only holding references to your actual objects, so which dictionary you use to update the object doesn't matter.
To update the object create a function that will update both the dictionaries
var oldObj = keyedObject[key];
timedObjects.Remove(oldObj.LastUpdateTime);
timedObjects.Add(myUpdatedObject.LastUpdateTime,myUpdatedObject);
keyedObject[key] = myUpdatedObject;
Now you have track of the same object by both time and key.
I am keeping only one reference to an object in timedObjects. This helps while removing.
You can keep trimming your timedObjects dictionary as required.
Ofcource, while trimming you must bear in mind that there is another dictionary keyedObject that has reference to the same object. Merely calling Remove will not be enough.
Your remove code will have to be like this:
removeObject = timedObjects[timeToRemove];
timedObjects.Remove(timeToRemove);
keyedObject.Remove(removeObject.key);
timeToRemove will mostly come from a for loop, where you decide which object to remove
The type of map you're looking for is (at least in Java) called a LinkedHashMap.
From the javadoc:
Hash table and linked list implementation of the Map interface, with
predictable iteration order. This implementation differs from HashMap
in that it maintains a doubly-linked list running through all of its
entries. This linked list defines the iteration ordering, which is
normally the order in which keys were inserted into the map
(insertion-order).
A special constructor is provided to create a linked hash map whose
order of iteration is the order in which its entries were last
accessed, from least-recently accessed to most-recently
(access-order). This kind of map is well-suited to building LRU
caches.
Source for LinkedHashMap from the OpenJDK
AFAIK, there are no existing implementations of a LinkedHashMap in C#. That being said, it shouldn't be terribly difficult to write one.
Instead of sorteddictionary, write your own linked list, and have the Dictionary point to its nodes as values. It will be always sorted by timestamp, updating timestamp and removing the least resently used element will be O(1).
here is the implementation of the LRU Cache in c#. efficient O(1), but not thread safe;
static void Main(string[] args)
{
var cache = new LruCache(3);
cache.Put(1, 1);
cache.Put(2, 2);
Console.WriteLine(cache.Get(1)); // returns 1
cache.Put(3, 3); // evicts key 2
Console.WriteLine(cache.Get(2)); // returns -1 (not found)
cache.Put(4, 4); // evicts key 1
Console.WriteLine(cache.Get(1)); // returns -1 (not found)
Console.WriteLine(cache.Get(3)); // returns 3
Console.WriteLine(cache.Get(4)); // returns 4
}
public class DoubleLinkedList
{
public int key;
public int value;
public DoubleLinkedList next;
public DoubleLinkedList prev;
public DoubleLinkedList(int k, int v)
{
key = k;
value = v;
}
}
public class LruCache
{
private int size;
private int capacity;
private Dictionary<int, DoubleLinkedList> map;
private DoubleLinkedList head;
private DoubleLinkedList tail;
public LruCache(int cap)
{
capacity = cap;
map = new Dictionary<int, DoubleLinkedList>();
head = new DoubleLinkedList(0, 0);
tail = new DoubleLinkedList(0, 0);
head.next = tail;
tail.prev = head;
}
public int Get(int key)
{
if (map.ContainsKey(key))
{
if (tail.prev.key != key)
{
var node = map[key];
RemoveNode(node);
AddToEnd(node);
}
return map[key].value;
}
return -1;
}
private void AddToEnd(DoubleLinkedList node)
{
var beforeTail = tail.prev;
node.prev = beforeTail;
beforeTail.next = node;
tail.prev = node;
node.next = tail;
}
private void RemoveNode(DoubleLinkedList node)
{
var before = node.prev;
before.next = node.next;
node.next.prev = before;
}
public void Put(int key, int value)
{
if (map.ContainsKey(key))
{
map[key].value = value;
var node = map[key];
RemoveNode(node);
AddToEnd(node);
}
else
{
size++;
if (size > capacity)
{
var node = head.next;
RemoveNode(node);
map.Remove(node.key);
size--;
}
var newNode = new DoubleLinkedList(key, value);
AddToEnd(newNode);
map.Add(key, newNode);
}
}
}

What to add for the update portion in ConcurrentDictionary AddOrUpdate

I am trying to re-write some code using Dictionary to use ConcurrentDictionary. I have reviewed some examples but I am still having trouble implementing the AddOrUpdate function. This is the original code:
dynamic a = HttpContext;
Dictionary<int, string> userDic = this.HttpContext.Application["UserSessionList"] as Dictionary<int, String>;
if (userDic != null)
{
if (useDic.ContainsKey(authUser.UserId))
{
userDic.Remove(authUser.UserId);
}
}
else
{
userDic = new Dictionary<int,string>();
}
userDic.Add(authUser.UserId, a.Session.SessionID.ToString());
this.HttpContext.Application["UserDic"] = userDic;
I don't know what to add for the update portion:
userDic.AddOrUpdate(authUser.UserId,
a.Session.SessionID.ToString(),
/*** what to add here? ***/);
Any pointers would be appreciated.
You need to pass a Func which returns the value to be stored in the dictionary in case of an update. I guess in your case (since you don't distinguish between add and update) this would be:
var sessionId = a.Session.SessionID.ToString();
userDic.AddOrUpdate(
authUser.UserId,
sessionId,
(key, oldValue) => sessionId);
I.e. the Func always returns the sessionId, so that both Add and Update set the same value.
BTW: there is a sample on the MSDN page.
I hope, that I did not miss anything in your question, but why not just like this? It is easier, atomic and thread-safe (see below).
userDic[authUser.UserId] = sessionId;
Store a key/value pair into the dictionary unconditionally, overwriting any value for that key if the key already exists: Use the indexer’s setter
(See: http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx)
The indexer is atomic, too. If you pass a function instead, it might not be:
All of these operations are atomic and are thread-safe with regards to all other operations on the ConcurrentDictionary. The only caveat to the atomicity of each operation is for those which accept a delegate, namely AddOrUpdate and GetOrAdd. [...] these delegates are invoked outside of the locks
See: http://blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx
I ended up implementing an extension method:
static class ExtensionMethods
{
// Either Add or overwrite
public static void AddOrUpdate<K, V>(this ConcurrentDictionary<K, V> dictionary, K key, V value)
{
dictionary.AddOrUpdate(key, value, (oldkey, oldvalue) => value);
}
}
For those who are interested in, I am currently implementing a case which is a great example for using the "oldValue" aka existing value instead of forcing a new one (personally I don't like the term "oldValue" as it is not that old when it was created just a few processor ticks ago from within a parallel thread).
dictionaryCacheQueues.AddOrUpdate(
uid,
new ConcurrentQueue<T>(),
(existingUid, existingValue) => existingValue
);

Enumerating through a Dictionary.KeyCollection in order

According to MSDN:
The order of the keys in the
Dictionary.KeyCollection is
unspecified
I am assuming that this is because additions to the Dictionary are placed into some sort of a hash table.
However, I would like to return the .Keys collection from a Dictionary as an IEnumerable (or perhaps as an ICollection) from a method, and enumerate through them in the order they were originally added to the Dictionary.
How best to accomplish this?
(I am using Winforms, .NET 2.0)
Then keep the keys separately in a List<T>. That original order no longer exists on the dictionary. The list will repeat insertion order.
You could use List<KeyValuePair<K,V>> in place of Dictionary<K,V> to maintain order. The problem with this of course is that it becomes harder to update the value for a key and the fact that you can have duplicate keys. But that can be handled with these extension methods
public static void AddOrUpdate<K, V>(this List<KeyValuePair<K, V>> list, K key, V value)
{
var pair = list.SingleOrDefault(kvp => kvp.Key.Equals(key));
if (!pair.Equals(null))
list.Remove(pair);
list.Add(new KeyValuePair<K, V>(key, value));
}
public static V GetValue<K, V>(this List<KeyValuePair<K, V>> list, K key)
{
var pair = list.SingleOrDefault(kvp => kvp.Key.Equals(key));
if (pair.Equals(null))
return default(V); //or throw an exception
return pair.Value;
}
public static bool ContainsKey<K, V>(this List<KeyValuePair<K, V>> list, K key)
{
return list.Any(kvp => kvp.Key.Equals(key));
}
Maybe SortedDictionary might do what you need if the key is the insertion order (1, 2, 3,... etc.)

Accessing a Dictionary.Keys Key through a numeric index

I'm using a Dictionary<string, int> where the int is a count of the key.
Now, I need to access the last-inserted Key inside the Dictionary, but I do not know the name of it. The obvious attempt:
int LastCount = mydict[mydict.keys[mydict.keys.Count]];
does not work, because Dictionary.Keys does not implement a []-indexer.
I just wonder if there is any similar class? I thought about using a Stack, but that only stores a string. I could now create my own struct and then use a Stack<MyStruct>, but I wonder if there is another alternative, essentially a Dictionary that implements an []-indexer on the Keys?
As #Falanwe points out in a comment, doing something like this is incorrect:
int LastCount = mydict.Keys.ElementAt(mydict.Count -1);
You should not depend on the order of keys in a Dictionary. If you need ordering, you should use an OrderedDictionary, as suggested in this answer. The other answers on this page are interesting as well.
You can use an OrderedDictionary.
Represents a collection of key/value
pairs that are accessible by the key
or index.
A Dictionary is a Hash Table, so you have no idea the order of insertion!
If you want to know the last inserted key I would suggest extending the Dictionary to include a LastKeyInserted value.
E.g.:
public MyDictionary<K, T> : IDictionary<K, T>
{
private IDictionary<K, T> _InnerDictionary;
public K LastInsertedKey { get; set; }
public MyDictionary()
{
_InnerDictionary = new Dictionary<K, T>();
}
#region Implementation of IDictionary
public void Add(KeyValuePair<K, T> item)
{
_InnerDictionary.Add(item);
LastInsertedKey = item.Key;
}
public void Add(K key, T value)
{
_InnerDictionary.Add(key, value);
LastInsertedKey = key;
}
.... rest of IDictionary methods
#endregion
}
You will run into problems however when you use .Remove() so to overcome this you will have to keep an ordered list of the keys inserted.
Why don't you just extend the dictionary class to add in a last key inserted property. Something like the following maybe?
public class ExtendedDictionary : Dictionary<string, int>
{
private int lastKeyInserted = -1;
public int LastKeyInserted
{
get { return lastKeyInserted; }
set { lastKeyInserted = value; }
}
public void AddNew(string s, int i)
{
lastKeyInserted = i;
base.Add(s, i);
}
}
You could always do this:
string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]
But I wouldn't recommend it. There's no guarantee that the last inserted key will be at the end of the array. The ordering for Keys on MSDN is unspecified, and subject to change. In my very brief test, it does seem to be in order of insertion, but you'd be better off building in proper bookkeeping like a stack--as you suggest (though I don't see the need of a struct based on your other statements)--or single variable cache if you just need to know the latest key.
I think you can do something like this, the syntax might be wrong, havent used C# in a while
To get the last item
Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last();
or use Max instead of Last to get the max value, I dont know which one fits your code better.
I agree with the second part of Patrick's answer. Even if in some tests it seems to keep insertion order, the documentation (and normal behavior for dictionaries and hashes) explicitly states the ordering is unspecified.
You're just asking for trouble depending on the ordering of the keys. Add your own bookkeeping (as Patrick said, just a single variable for the last added key) to be sure. Also, don't be tempted by all the methods such as Last and Max on the dictionary as those are probably in relation to the key comparator (I'm not sure about that).
In case you decide to use dangerous code that is subject to breakage, this extension function will fetch a key from a Dictionary<K,V> according to its internal indexing (which for Mono and .NET currently appears to be in the same order as you get by enumerating the Keys property).
It is much preferable to use Linq: dict.Keys.ElementAt(i), but that function will iterate O(N); the following is O(1) but with a reflection performance penalty.
using System;
using System.Collections.Generic;
using System.Reflection;
public static class Extensions
{
public static TKey KeyByIndex<TKey,TValue>(this Dictionary<TKey, TValue> dict, int idx)
{
Type type = typeof(Dictionary<TKey, TValue>);
FieldInfo info = type.GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance);
if (info != null)
{
// .NET
Object element = ((Array)info.GetValue(dict)).GetValue(idx);
return (TKey)element.GetType().GetField("key", BindingFlags.Public | BindingFlags.Instance).GetValue(element);
}
// Mono:
info = type.GetField("keySlots", BindingFlags.NonPublic | BindingFlags.Instance);
return (TKey)((Array)info.GetValue(dict)).GetValue(idx);
}
};
One alternative would be a KeyedCollection if the key is embedded in the value.
Just create a basic implementation in a sealed class to use.
So to replace Dictionary<string, int> (which isn't a very good example as there isn't a clear key for a int).
private sealed class IntDictionary : KeyedCollection<string, int>
{
protected override string GetKeyForItem(int item)
{
// The example works better when the value contains the key. It falls down a bit for a dictionary of ints.
return item.ToString();
}
}
KeyedCollection<string, int> intCollection = new ClassThatContainsSealedImplementation.IntDictionary();
intCollection.Add(7);
int valueByIndex = intCollection[0];
The way you worded the question leads me to believe that the int in the Dictionary contains the item's "position" on the Dictionary. Judging from the assertion that the keys aren't stored in the order that they're added, if this is correct, that would mean that keys.Count (or .Count - 1, if you're using zero-based) should still always be the number of the last-entered key?
If that's correct, is there any reason you can't instead use Dictionary<int, string> so that you can use mydict[ mydict.Keys.Count ]?
I don't know if this would work because I'm pretty sure that the keys aren't stored in the order they are added, but you could cast the KeysCollection to a List and then get the last key in the list... but it would be worth having a look.
The only other thing I can think of is to store the keys in a lookup list and add the keys to the list before you add them to the dictionary... it's not pretty tho.
To expand on Daniels post and his comments regarding the key, since the key is embedded within the value anyway, you could resort to using a KeyValuePair<TKey, TValue> as the value. The main reasoning for this is that, in general, the Key isn't necessarily directly derivable from the value.
Then it'd look like this:
public sealed class CustomDictionary<TKey, TValue>
: KeyedCollection<TKey, KeyValuePair<TKey, TValue>>
{
protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
{
return item.Key;
}
}
To use this as in the previous example, you'd do:
CustomDictionary<string, int> custDict = new CustomDictionary<string, int>();
custDict.Add(new KeyValuePair<string, int>("key", 7));
int valueByIndex = custDict[0].Value;
int valueByKey = custDict["key"].Value;
string keyByIndex = custDict[0].Key;
A dictionary may not be very intuitive for using index for reference but, you can have similar operations with an array of KeyValuePair:
ex.
KeyValuePair<string, string>[] filters;
You can also use SortedList and its Generic counterpart. These two classes and in Andrew Peters answer mentioned OrderedDictionary are dictionary classes in which items can be accessed by index (position) as well as by key. How to use these classes you can find: SortedList Class , SortedList Generic Class .
Visual Studio's UserVoice gives a link to generic OrderedDictionary implementation by dotmore.
But if you only need to get key/value pairs by index and don't need to get values by keys, you may use one simple trick. Declare some generic class (I called it ListArray) as follows:
class ListArray<T> : List<T[]> { }
You may also declare it with constructors:
class ListArray<T> : List<T[]>
{
public ListArray() : base() { }
public ListArray(int capacity) : base(capacity) { }
}
For example, you read some key/value pairs from a file and just want to store them in the order they were read so to get them later by index:
ListArray<string> settingsRead = new ListArray<string>();
using (var sr = new StreamReader(myFile))
{
string line;
while ((line = sr.ReadLine()) != null)
{
string[] keyValueStrings = line.Split(separator);
for (int i = 0; i < keyValueStrings.Length; i++)
keyValueStrings[i] = keyValueStrings[i].Trim();
settingsRead.Add(keyValueStrings);
}
}
// Later you get your key/value strings simply by index
string[] myKeyValueStrings = settingsRead[index];
As you may have noticed, you can have not necessarily just pairs of key/value in your ListArray. The item arrays may be of any length, like in jagged array.

Categories

Resources