9.3. Dictionary
We have explored several ways of storing a collection of the same type of data:
arrays: built-in syntax, unchanging size of the collection
List: generic class type, allows the size of the collection to grow
Both approaches allow reference to data elements using a
numerical index between square brackets, as in words[i]
.
The index provides an order for the elements,
but there is no meaning to the index beyond the sequence order.
Sometimes, we want to look up data based on a more meaningful key, as in a dictionary: given a word, you can look up the definition.
C# uses the type name Dictionary, but with greater generality than in
nontechnical use. In a regular dictionary, you start with a word,
and look up the definition. The generalization is to have some piece of
data that leads you to (or maps to) another piece of data.
The computer science jargon is that a key leads you to a value.
In a normal dictionary, these are both likely to be strings, but in the
C# generalization, the possible types of key and value are much more extensive.
Hence the generic Dictionary
type requires you to specify
both a type for the key and a type for the value.
9.3.1. Creating a Dictionary
We can initialize an English-Spanish dictionary e2sp
with
Dictionary<string, string> e2sp = new Dictionary<string, string>();
That is quite a mouthful! The C# var
keyword (implicitly-typed local variable) syntax
is handy to shorten it as you do not have to repeat the type name:
var e2sp = new Dictionary<string, string>();
The general generic type syntax therefore is:
Dictionary<
keyType,
valueType>
For example, if you are counting the number of repetitions of words in a document, you are
likely to want a Dictionary
mapping each word to its number of repetitions so far:
var wordCount = new Dictionary<string, int>();
If your friends all have a personal list of phone numbers, you might look them up
with a dictionary with a string name for the key and a List
of personal phone number
strings for the value. The type could be Dictionary<string, List<string>>
.
This example illustrates how one generic type can be built on another.
There is no restriction on the value type. There is one important technical restriction on the key type: it should be immutable. .. This has to do with the implementation referenced in dictionary-efficiency.
9.3.2. Accessing Data in Dictionary
Similar to an array or List
, you can assign and reference elements of
a Dictionary
, using square bracket notation. The difference is that the
reference is through a key, not a sequential index, as in:
e2sp["one"] = "uno";
e2sp["two"] = "dos";
or, you can use the Dictionary.Add() method:
e2sp.Add("three", "tres");
csharprepl displays dictionaries in its own special form, as a table showing the {key, value} pairs and type. Here is a longer csharprepl sequence:
> Dictionary<string, string> e2sp = new Dictionary<string, string>();
> e2sp
Dictionary<string, string>(0)
> e2sp["one"] = "uno";
> e2sp["two"] = "dos";
> e2sp.Add("three", "tres");
> e2sp.Count // the Count property gives the length of the dictionary
3
> e2sp;
Dictionary<string, string>(3)
┌──────┬─────────────────────┬──────────────────────────────┐
│ Name │ Value │ Type │
├──────┼─────────────────────┼──────────────────────────────┤
│ [0] │ { "one", "uno" } │ KeyValuePair<string, string> │
│ [1] │ { "two", "dos" } │ KeyValuePair<string, string> │
│ [2] │ { "three", "tres" } │ KeyValuePair<string, string> │
└──────┴─────────────────────┴──────────────────────────────┘
> Console.WriteLine("{0}, {1}; {2}", e2sp["one"], e2sp["two"], e2sp["three"])
uno, dos; tres
As you can see, we using the key to refer to the value of a dictionary entry. If a key-value pair already exists, you may not Add() a value to the key, but you can update date it using the Item[] property (the indexer[] square brackets) such as dict[“aaa”] = “AAA”;
If you want to iterate through a whole Dictionary
, you will want the syntax below,
with foreach
and the property Keys
:
> foreach (string s in e2sp.Keys) {
> Console.WriteLine(s);
> }
one
two
three
To loop through the dictionary and access both the key and value in each entry, you may do:
> foreach (var entry in e2sp.Keys) {
> Console.WriteLine("{0} : {1}", entry.Key, entry.Value);
> }
In this example, we use an implicitly-typed variable entry
to ask the compiler to infer the type.
We then use the .Key and .Value properties to refer to the data.
The documentation for Dictionary
says
that you cannot depend on the order of processing with foreach
, though the present
implementation remembers the order in which keys were added.
9.3.3. Properties and Methods in Dictionary
There are plenty of properties and methods built in in the Dictionary class in addition to .Add() and .Count, .Key, .Value as aforementioned.
It is often useful to know if a key is already in a Dictionary
:
Note the method ContainsKey
:
> e2sp.ContainsKey("seven")
false
> e2sp.ContainsKey("three")
true
The method Remove
takes a key as parameter. Like a List
and other
collections, a Dictionary
has a Clear
method:
> e2sp.Count;
┌───────────────────────────────────────────────────CompilationErrorException────────────────────────────────────────────────────┐
│ (1,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ e2sp.Count
> e2sp.Count
3
> e2sp.Remove("two")
true
> e2sp.Count
2
> e2sp
Dictionary<string, string>(2)
┌──────┬─────────────────────┬──────────────────────────────┐
│ Name │ Value │ Type │
├──────┼─────────────────────┼──────────────────────────────┤
│ [0] │ { "one", "uno" } │ KeyValuePair<string, string> │
│ [1] │ { "three", "tres" } │ KeyValuePair<string, string> │
└──────┴─────────────────────┴──────────────────────────────┘
> e2sp.Clear()
> e2sp
Dictionary<string, string>(0)
> e2sp.Count
0