Dictionary
In VB.NET the Dictionary
allows fast key lookups. A generic type, it can use any types for its keys and values. Its syntax is at first confusing.
Compared to alternatives, a Dictionary
is easy to use and effective. It has many functions (like ContainsKey
and TryGetValue
) that do lookups.
Most Dictionaries we use will be added to with the Add()
Subroutine. Usually we create an empty Dictionary
and then populate it with keys and values.
Dictionary
with String
keys, and Integer values. The Dictionary
is empty here.Dictionary
. For the arguments, we pass the key we want to add, and the value.Count
of this dictionary, after 4 Add()
calls have run, is 4—a key and value are counted together as 1 entry.Module Module1 Sub Main() ' Step 1: create a Dictionary. Dim dictionary As New Dictionary(Of String, Integer) ' Step 2: add 4 entries. dictionary.Add("bird", 20) dictionary.Add("frog", 1) dictionary.Add("snake", 10) dictionary.Add("fish", 2) ' Step 3: display count. Console.WriteLine("DICTIONARY COUNT: {0}", dictionary.Count) End Sub End ModuleDICTIONARY COUNT: 4
TryGetValue
Often we test a value through a key in a Dictionary
collection. Then we act upon that value. The TryGetValue
function combines the 2 steps—this enables optimizations.
Dictionary
with string
keys and values, and then add 2 pairs to it. We then invoke TryGetValue
and print the result.GetValueOrDefault
for a more convenient way to access values in a VB.NET dictionary.Module Module1 Sub Main() Dim values As Dictionary(Of String, String) = New Dictionary(Of String, String) values.Add("A", "uppercase letter A") values.Add("c", "lowercase letter C") ' Get value with TryGetValue. Dim result As String = Nothing If values.TryGetValue("c", result) Then Console.WriteLine("RESULT: {0}", result) End If End Sub End ModuleRESULT: lowercase letter C
ContainsKey
This function returns a Boolean
value, which means you can use it in an If conditional statement. One common use of ContainsKey
is to prevent exceptions before calling Add.
Module Module1 Sub Main() ' Declare new Dictionary with String keys. Dim dictionary As New Dictionary(Of String, Integer) ' Add two keys. dictionary.Add("carrot", 7) dictionary.Add("perl", 15) ' See if this key exists. If dictionary.ContainsKey("carrot") Then ' Write value of the key. Dim num As Integer = dictionary.Item("carrot") Console.WriteLine(num) End If ' See if this key also exists (it doesn't). If dictionary.ContainsKey("python") Then Console.WriteLine(False) End If End Sub End Module7
If you add keys to the Dictionary
and one is already present, you will get an exception. We often must check with ContainsKey
that the key is not present.
Module Module1 Sub Main() Dim lookup As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer) lookup.Add("cat", 10) ' This causes an error. lookup.Add("cat", 100) End Sub End ModuleUnhandled Exception: System.ArgumentException: An item with the same key has already been added. at System.ThrowHelper.ThrowArgumentException...
KeyNotFoundException
To see if a key exists in a Dictionary
, we should use ContainsKey
or TryGetValue
. If we just access the key directly, we might get a KeyNotFoundException
.
KeyNotFoundException
, but this will be slower than not causing an exception.Module Module1 Sub Main() Dim dict = New Dictionary(Of String, String)() ' We must use ContainsKey or TryGetValue. If dict("car") = "vehicle" Then Return End If End Sub End ModuleUnhandled Exception: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. at System.Collections.Generic.Dictionary`2.get_Item(TKey key)...
The Item member is a Property accessor. It sets or gets an element in the Dictionary
. It is equivalent to Add when you assign it.
Add()
may be a better choice than assigning to a key in a Dictionary
—it will warn you with an exception if you have a duplicate.Module Module1 Sub Main() Dim dictionary = New Dictionary(Of Integer, Integer)() ' Add data by assigning to a key. dictionary(10) = 20 ' Look up value. Console.WriteLine(dictionary(10)) End Sub End Module20
For Each
loopWe can loop over the entries in a Dictionary
. It is usually easiest to use the For Each
loop. We can access each individual KeyValuePair
structure in the loop body.
Dictionary
and add 4 color names to it—blue, yellow, green and red.Dictionary
keys and values—no casts are required.Module Module1 Sub Main() ' Step 1: create Dictionary with 4 keys. Dim colors As New Dictionary(Of String, Integer) colors.Add("blue", 32) colors.Add("yellow", 16) colors.Add("green", 256) colors.Add("red", 100) ' Step 2: use For Each loop over pairs. For Each pair As KeyValuePair(Of String, Integer) In colors Console.WriteLine("COLOR: {0}, VALUE: {1}", pair.Key, pair.Value) Next End Sub End ModuleCOLOR: blue, VALUE: 32 COLOR: yellow, VALUE: 16 COLOR: green, VALUE: 256 COLOR: red, VALUE: 100
Keys
We can get a List
of the Dictionary
keys. Dictionary
has a Keys
property, and we can use this with types like the List
that act on IEnumerable
.
Dictionary
and place 4 String
keys (which are all animal names) in it, with Integer values.List
constructor on the Keys
property. The keys have the same type as that from the source Dictionary
.Item()
we can access the value from the Dictionary
based on the String
key.Module Module1 Sub Main() ' Step 1: add 4 string keys. Dim animals As New Dictionary(Of String, Integer) animals.Add("bird", 12) animals.Add("frog", 11) animals.Add("cat", 10) animals.Add("elephant", -11) ' Step 2: get List of String Keys. Dim list As New List(Of String)(animals.Keys) ' Step 3: loop over Keys and print the Dictionary values. For Each value As String In list Console.WriteLine("ANIMAL: {0}, VALUE: {1}", value, animals.Item(value)) Next End Sub End Moduleplease, 12 help, 11 poor, 10 people, -11
Dictionary
uses typed keys and values. We specify these types when the Dictionary
is declared. Here we use more a more complex value type, a String
array.
Dictionary
. It has Integer keys and String
array values—so each int
can point to an entire array.ContainsKey
. When the key type is Integer, we must pass an Integer to ContainsKey
.String
array value with Item, and then call String.Join
to concatenate its values.Module Module1 Sub Main() ' Step 1: use Integer keys, String array values. Dim dictionary As New Dictionary(Of Integer, String()) dictionary.Add(100, New String() {"cat", "bird"}) dictionary.Add(200, New String() {"dog", "fish"}) ' Step 2: see if key exists. If dictionary.ContainsKey(200) Then ' Step 3: get array value, join elements, and print it. Dim value() As String = dictionary.Item(200) Console.WriteLine("RESULT: {0}", String.Join(",", value)) End If End Sub End ModuleRESULT: dog,fish
ContainsValue
This returns a Boolean
that tells whether any value in the Dictionary
is equal to the argument. It is implemented as a For
-loop over the entries in the Dictionary
.
ContainsValue
has no performance advantage over a List
that uses linear searching. Accessing keys in the Dictionary
is faster.Module Module1 Sub Main() ' Create new Dictionary with Integer values. Dim dictionary As New Dictionary(Of String, Integer) dictionary.Add("pelican", 11) dictionary.Add("robin", 21) ' See if Dictionary contains the value 21 (it does). If dictionary.ContainsValue(21) Then ' Prints true. Console.WriteLine(True) End If End Sub End ModuleTrue
Remove
Here we use Remove
. You must pass one parameter to this method, indicating which key you want to have removed from the Dictionary
instance.
Module Module1 Sub Main() ' Create Dictionary and add two keys. Dim dictionary As New Dictionary(Of String, Integer) dictionary.Add("fish", 32) dictionary.Add("microsoft", 23) ' Remove two keys. dictionary.Remove("fish") ' Will remove this key. dictionary.Remove("apple") ' Doesn't change anything. End Sub End Module
It is possible to copy the entire contents of a Dictionary
. You can do this by declaring a new Dictionary
reference and using the copy constructor.
Dictionary
constructor, pass the Dictionary
you want to copy as the parameter.Module Module1 Sub Main() Dim source = New Dictionary(Of String, Integer)() source.Add("bird", 20) ' Copy the Dictionary. Dim copy = New Dictionary(Of String, Integer)(source) ' Write some details. Console.WriteLine("COPY: {0}, COUNT = {1}", copy("bird"), copy.Count) End Sub End ModuleCOPY: 20, COUNT = 1
We use a Dictionary
in a class
. We store it as Private member variable, and then access it through Public methods on the enclosing class
.
Main
Sub
. We create a new instance of the Example class
.New()
Sub
in the Example class
. The Dictionary
field is created. We add 3 entries to the Dictionary
.GetValue()
on the Example instance. This returns a value from the Dictionary
that was stored with the String
key "make."Module Module1 Class Example Private _dictionary Public Sub New() ' Part 2: allocate and populate the field Dictionary. Me._dictionary = New Dictionary(Of String, Integer) Me._dictionary.Add("make", 55) Me._dictionary.Add("model", 44) Me._dictionary.Add("color", 12) End Sub Public Function GetValue() As Integer ' Return value from private Dictionary. Return Me._dictionary.Item("make") End Function End Class Sub Main() ' Part 1: allocate an instance of the class. Dim example As New Example ' Part 3: write a value from the class. Console.WriteLine(example.GetValue()) End Sub End Module55
Count
You can count the number of entries with the Count
property. Internally, the Count
property subtracts 2 integers—it does no looping.
Module Module1 Sub Main() Dim dictionary As New Dictionary(Of String, Integer) dictionary.Add("a", 5) dictionary.Add("b", 8) dictionary.Add("c", 13) dictionary.Add("d", 14) ' Get count. Console.WriteLine(dictionary.Count) End Sub End Module4
ToDictionary
We can quickly construct a new Dictionary
from a collection (array, List
) with the ToDictionary
extension method. ToDictionary
returns a Dictionary
.
ToDictionary
has 2 lambda arguments. They both receive one argument: the String
from the source array.String
(for the first, key selector function) and an Integer (for the value selector function).Module Module1 Sub Main() ' Create an array of four string literal elements. Dim array() As String = {"dog", "cat", "rat", "mouse"} ' Use ToDictionary. ' ... Use each string as the key. ' ... Use each string length as the value. Dim dict As Dictionary(Of String, Integer) = array.ToDictionary(Function(value As String) Return value End Function, Function(value As String) Return value.Length End Function) ' Display dictionary. For Each pair In dict Console.WriteLine(pair) Next End Sub End Module[dog, 3] [cat, 3] [rat, 3] [mouse, 5]
ContainsKey
Dictionary
is an optimization for key lookups. It can make a slow program many times faster. Consider this benchmark, which tests Dictionary
and List
.
ContainsKey
on a Dictionary
with 1000 String
keys.Contains
to find a String
key in a List
that exists. The element is the 900th one in the List
.Dictionary
is faster. In the List
900 items are looped over, but with hashing in the Dictionary
, fewer items are scanned.Module Module1 Sub Main() ' Create Dictionary and List. Dim lookup As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer) For i As Integer = 0 To 1000 lookup.Add(i.ToString(), 1) Next Dim list As List(Of String) = New List(Of String) For i As Integer = 0 To 1000 list.Add(i.ToString()) Next Dim m As Integer = 1000 ' Version 1: search Dictionary. Dim s1 As Stopwatch = Stopwatch.StartNew For i As Integer = 0 To m - 1 If Not lookup.ContainsKey("900") Then Return End If Next s1.Stop() Dim s2 As Stopwatch = Stopwatch.StartNew ' Version 2: search List. For i As Integer = 0 To m - 1 If Not list.Contains("900") Then Return End If Next s2.Stop() Dim u As Integer = 1000000 Console.WriteLine(((s1.Elapsed.TotalMilliseconds * u) / m).ToString("0.00 ns")) Console.WriteLine(((s2.Elapsed.TotalMilliseconds * u) / m).ToString("0.00 ns")) End Sub End Module 35.10 ns Dictionary ContainsKey (1000 keys) 6801.10 ns List Contains (1000 elements)
This generic type is powerful. Designed for super-fast lookups, Dictionary
often improves the performance of programs. And it can simplify our logic by checking for duplicates.