C# Dictionary

Fast lookups are critical. The Dictionary type in the C# language provides fast lookups with keys to get values. It allows you to use keys and values of any type, including ints and strings. Dictionary requires a special syntax form that may be unfamiliar.

Key points:Dictionary is used when you have many different elements. You must specify the types of its keys and values.

Add keys

To get started, let's add four keys with values to a Dictionary instance. Afterwards, we look inside the Dictionary using Visual Studio's debugger. You will see that the Dictionary is composed of separate keys and values. Here's the Dictionary code and then its internal view.

Program that uses Dictionary Add method [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	Dictionary<string, int> dictionary =
	    new Dictionary<string, int>();
	dictionary.Add("cat", 2);
	dictionary.Add("dog", 1);
	dictionary.Add("llama", 0);
	dictionary.Add("iguana", -1);
    }
}

Results
    Dictionary contains four keys, each associated with one value.

Inside the Dictionary. Here's what the above code looks like in memory. The Dictionary instance is represented by a collection of key and value pairs. The screenshot is worth looking at.

Dictionary internals

Look up values

Next, you can check to see if a given string is present in a Dictionary with string keys. We look at more types of Dictionaries further on, but here is the ContainsKey method.

Program that uses ContainsKey [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	Dictionary<string, int> dictionary = new Dictionary<string, int>();
	dictionary.Add("apple", 1);
	dictionary.Add("windows", 5);

	// See whether Dictionary contains this string.
	if (dictionary.ContainsKey("apple"))
	{
	    int value = dictionary["apple"];
	    Console.WriteLine(value);
	}

	// See whether Dictionary contains this string.
	if (!dictionary.ContainsKey("acorn"))
	{
	    Console.WriteLine(false);
	}
    }
}

Output

1
False

Efficiency. There is a more efficient method called TryGetValue on the Dictionary class. You should definitely use it when possible. As its name implies, it tests for the key and then returns the value if it finds the key.

TryGetValue Method

KeyValuePair

KeyValuePair (Key and Value properties)

This tip is not in every beginner's C# book. When Dictionary, or any object that implements IDictionary, is used in a foreach loop, it returns an enumeration. In the case of Dictionary, this enumeration is in the form of KeyValuePair values.

KeyValuePair Hints

KeyNotFoundException

If you are running into the KeyNotFoundException, you are accessing a key in your Dictionary that doesn't exist. Dictionary is not the same as Hashtable and you must test keys for existence first, with ContainsKey or TryGetValue.

KeyNotFoundException Fix

Foreach

Foreach loop construct

Here we use foreach syntax and KeyValuePair generics in the foreach loop. With collections like Dictionary, we must always know the value types. With each KeyValuePair, there is a Key member and Value member.

Foreach Loop Examples
Program that uses foreach on Dictionary [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	// Example Dictionary again
	Dictionary<string, int> d = new Dictionary<string, int>()
	{
	    {"cat", 2},
	    {"dog", 1},
	    {"llama", 0},
	    {"iguana", -1}
	};
	// Loop over pairs with foreach
	foreach (KeyValuePair<string, int> pair in d)
	{
	    Console.WriteLine("{0}, {1}",
		pair.Key,
		pair.Value);
	}
	// Use var keyword to enumerate dictionary
	foreach (var pair in d)
	{
	    Console.WriteLine("{0}, {1}",
		pair.Key,
		pair.Value);
	}
    }
}

Output

cat, 2
dog, 1
llama, 0
iguana, -1

cat, 2
dog, 1
llama, 0
iguana, -1
Loop illustrated by arrow

The code example declares and populates an example Dictionary. This Dictionary happens to indicate what animals we have and how many of them.

Using the foreach loop. It has a ShowDictionaryPair method. This method demonstrates the foreach loop and the KeyValuePair declaration.

Tip:Pay careful attention to the syntax in the foreach loop. Each KeyValuePair has two members, pair.Key and pair.Value, which contain string keys and int values.

Var keyword

Using the var keyword. The final loop in the code shows how you can make the syntax for looping really simple by using the var keyword. This is not always desirable on some projects.

Var Examples

Get keys

Here we use the Keys property and then look through each key and lookup the values. This method is slower but has the same results. Using the Keys collection and putting it in an array or List is very effective in other situations.

Program that gets Keys from Dictionary [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	Dictionary<string, int> d = new Dictionary<string, int>()
	{
	    {"cat", 2},
	    {"dog", 1},
	    {"llama", 0},
	    {"iguana", -1}
	};
	// Store keys in a List
	List<string> list = new List<string>(d.Keys);
	// Loop through list
	foreach (string k in list)
	{
	    Console.WriteLine("{0}, {1}",
		k,
		d[k]);
	}
    }
}

Output

cat, 2
dog, 1
llama, 0
iguana, -1

Benchmark loops

Performance optimization

Using foreach on KeyValuePairs is several times faster than using Keys. This is probably because the Keys collection is not used. KeyValuePair allows us to simply look through each pair one at a time. This avoids lookups and using the garbage-collected heap for storage.

Benchmark for KeyValuePair foreach loop

KeyValuePair: 125 ms
Note:         This loops through the pairs in the Dictionary.

Keys loop:    437 ms
Note:         This gets the Keys, then loops through them.
	      It does another lookup for the value.

Note on the benchmark. I made a small change to the Keys version for clarity and performance, so these figures are only general and apply to the previous version. Using KeyValuePair is still faster.

Sort

Sorted letters: A through Z

If you need to sort the values in your Dictionary, you may be perplexed at first and wonder how to order the keys properly. Fortunately, I have an article about how to do this, although it is not optimal.

Sort Dictionary

Different types

Dictionary in C# is a generic class. This means it requires you to specify a type for it to use. So, you can use an int key, just as easily as a string key. Here is an example of a Dictionary with int keys.

Program that uses int keys [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	// Use a dictionary with an int key.
	Dictionary<int, string> dict = new Dictionary<int, string>();
	dict.Add(100, "Bill");
	dict.Add(200, "Steve");
	// You can lookup the int in the dictionary.
	if (dict.ContainsKey(200))
	{
	    Console.WriteLine(true);
	}
    }
}

Output

True

For more advanced developers, you can use the GetHashCode method and override it to create Dictionaries or hashes with the class. This can improve performance in those cases.

Lookup speed

To enhance lookup speed on your Dictionary, you can change the size of the keys you use. My research has shown that when you use shorter string keys, the lookup time is improved. This could create more collisions, so testing may be necessary.

Dictionary String Key Length

LINQ

LINQ (language integrated query)

This example program uses the ToDictionary method. ToDictionary is an extension method on IEnumerable that places the keys and values into a new Dictionary. The program uses lambda expressions.

Program that uses LINQ [C#]

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
	string[] arr = new string[]
	{
	    "One",
	    "Two"
	};
	var dict = arr.ToDictionary(item => item, item => true);
	foreach (var pair in dict)
	{
	    Console.WriteLine("{0}, {1}",
		pair.Key,
		pair.Value);
	}
    }
}

Output

One, True
Two, True

The above example uses ToDictionary, which resides in the System.Linq namespace, on the string[] array to create a lookup table where both strings can be accessed in constant time, O(1).

ToDictionary Method

ContainsValue

Dictionary also helpfully implements a method called ContainsValue. This method does not enjoy the constant-time lookup speed that ContainsKey has. It instead searches the entire collection. It is linear in complexity.

Program that uses ContainsValue [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	Dictionary<string, int> d = new Dictionary<string, int>();
	d.Add("cat", 1);
	d.Add("dog", 2);
	if (d.ContainsValue(1))
	{
	    Console.WriteLine(true); // true
	}
    }
}

Output

True

ContainsValue method. The above example will internally loop through all elements in the Dictionary until it finds the match, or there are no more to check. MSDN states that "this method is an O(n) operation, where n is Count."

ContainsValue Method

Indexer

Note

Instead of calling Add on a Dictionary instance to add a new value, you can use the indexer with the [ ] brackets. This syntax can also be used to get the value at the key you specify.

Please note that if you try to get a value at a key that doesn't exist, an exception is thrown. The big difference between calling Add and assigning to an index is that an exception is not thrown when you assign to a key that already has a value. With Add, an exception is thrown.

Program that uses Dictionary indexer [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	Dictionary<int, int> dictionary = new Dictionary<int, int>();

	// You can assign with the indexer.
	dictionary[1] = 2;
	dictionary[2] = 1;
	dictionary[1] = 3; // Reassign.

	// You can read with the indexer.
	// ... If you read an element that doesn't exist, you get an exception.
	Console.WriteLine(dictionary[1]);
	Console.WriteLine(dictionary[2]);
    }
}

Output

3
1

Clear and Count

This section provides information

You can erase all the key/value pairs within your Dictionary by using the Clear method, which accepts no parameters. Alternatively you can assign the variable to null. The difference between Clear and null is not important for memory, as in either case the entries are garbage-collected.

Internally:We find that Clear calls Array.Clear, which is not managed code.

The Count method on the Dictionary collection is an effective way of computing the total number of keys in the instance. This is much simpler than accessing the Keys property or looping over the Dictionary to count it.

Count Dictionary

Remove entry

Here you want to eliminate an entry, not just by setting its value to null or string.Empty, but by also removing the key itself. Fortunately you can use the Remove method.

Program that uses Remove [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	Dictionary<string, int> d = new Dictionary<string, int>();
	d.Add("cat", 1);
	d.Add("dog", 2);

	d.Remove("cat"); // Removes cat
	d.Remove("nothing"); // Doesn't remove anything
    }
}

Result
    The key "cat" is removed.
Visual Studio logo (Copyright Microsoft)

Running the code in Visual Studio, no exceptions are thrown, which means that when you remove a key that doesn't exist, nothing happens.

However:Remove throws System.ArgumentNullException when it receives a null parameter.

Copy

The Dictionary class has a useful constructor that allows you to easily copy all the values and keys in your Dictionary into a new Dictionary instance. You can write the logic yourself, but using this constructor improves code reuse and simplicity.

Copy Dictionary

Note:This site has more information on the Dictionary copy constructor.

Return

Return keyword

It is also possible to use the Dictionary constructed type as an argument to methods or as a return value from methods or properties. The Dictionary type is defined as a class. It is always passed as a reference type. This means only 32-64 bits will be copied on the method invocation.

And:The same principles apply when copying a Dictionary return value from a method.

Return Statement

List versus Dictionary

Question and answer

I suggest you almost always use Dictionary when you need to do lookups. If you use List and you need to look up a key, your program may freeze if you happen to have a huge number of elements. In other words, if you use Dictionary, your program can recover from pathological, edge cases.

List Examples

On the other hand, it is somewhat faster to loop through all the elements in a List than in a Dictionary. So if looping through elements is the most common operation, a List is superior.

Dictionary Versus List Loop

You will find other collections, such as SortedDictionary, in the .NET Framework available for you to use. My experience is that it is hard to get equivalent performance as with Dictionary.

SortedDictionary

Composite keys

Programming tip

You can sometimes use multiple variables in a key by creating a special function that transforms those variables into a string, serializing them. So, you could use the string "1,2" to mean the ints 1 and 2. This approach is similar to how composite names in programming languages use a period as the separator (such as Type.Member).

Initialize field

Sometimes it is useful to have a Dictionary in your class that is allocated at the class level, not in a method or constructor. Additionally, if you have a static class then you should always initialize your Dictionary at the class level like this instead of the static constructor.

Also:Static constructors have performance penalties, which I have measured.

Program that uses Dictionary with class [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	Example e = new Example();
	Console.WriteLine(e.GetValue());
    }
}

class Example
{
    Dictionary<int, int> _d = new Dictionary<int, int>()
    {
	{1, 1},
	{2, 3},
	{3, 5},
	{6, 10}
    };
    public int GetValue()
    {
	return _d[2]; // Example only
    }
}

Output

3

Summary

.NET Framework information

We saw how you can use Dictionary with KeyValuePair to look through all pairs in the Dictionary. Additionally we tested ContainsKey and TryGetValue, which let you check key existence.

Thus:With their dramatic performance advantages and implementation details, collections such as Dictionary are some of the most interesting in computer science.

Collections
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值