(.NET 2.) Dictionary(Of Key, Value) performance trick

(I used the VB.NET notation in the title so it won't nag about illegal characters like < and >)

In .NET 2.0 you'll have this new generic Hashtable variant called Dictionary<TKey, TValue>. It can be used as a Hashtable but now strongly typed and it doesn't use boxing anymore with value types. That's great and all, though there's one thing that's different from the hashtable: retrieving a value with a key that's not there. If the key value isn't present in the Dictionary, you'll get an exception when you do:

MyClass myValue = myDictionary[key];
and key is not present in myDictionary, while with the hashtable, you'll get back a null value. See my old blogpost about that.

What's often overlooked is a performance penalty with this pattern shift. Because of the exception, people will do something like this:

MyClass toReturn;
if(!_myDictionary.ContainsKey(key))
{
	toReturn = _myDictionary[key];
}
else
{
	toReturn = new MyClass();
}
Pretty obvious code, right? Well, ok, but it has a performance penalty which isn't that obvious: you'll query the key index twice: once in the ContainsKey call and once in the _myDictionary[key] call. If you query your dictionary a lot of times, even if the dictionary is fairly small, it will hurt performance. Better do:
MyClass toReturn = null;
if(!_myDictionary.TryGetValue(key, out toReturn))
{
	toReturn = new MyClass();
}
During profile sessions of my code I found a lot of these misusages in my .NET 2.0 versions of ported .NET 1.1 code. So, be smart, and do it right the first time with TryGetValue, it's your friend.

About the lack of blogging lately: I was very busy with finalizing the beta for LLBLGen Pro v2.0, which first beta was released 2 days ago. It's still pretty hectic, but I hope to have more time soon to blog more!

6 Comments

  • That sucks. I hadn't noticed that yet. I usually have some code like:

    Customer cust = (Customer)customers[custName];

    if (cust!=null)

    ...

    The whole try thing with an out parameter is so much more verbose. And it makes upgrading to use generic hashtables a heck of a lot more work.

  • Nice tip! I didn't notice this has a performance impact. Now i have to do some refactoring. ;-)

  • PowerCollections.NET (Which I wouln't consider living without) extends this with a GetValueElseAdd operation, which is often really what you're looking to do in this instance.



    (It's missing on some containers, for reasons I cant quite fathom, but it can be implemented on those by using View. I must ask on the PC.NET forum why this is.)

  • I have a dictionary declared like this.

    Dictionary^ controlPool = gcnew Dictionary();

    controlPool->Add("PFWUtilities",tools);

    then later on I have:

    PFWUtilities^ tools;

    passedControls->TryGetValue("PFWUtilities",tools)

    tools never gets any value. I'm using c++/cli. It compiles fine but the value never comes through.

    Any ideas?

  • I tried this as well... no luck

    passedControls->TryGetValue("PFWUtilities", static_cast(tools));

  • What I have to do to get the value is this:

    tools = static_cast(passedControls["PFWUtilities"]);

    but I lose the benefit of the trygetvalue. Anyone have any ideas?

Comments have been disabled for this content.