aAbrEABBBEAfAAfCfAsArX-~C| 788WCZ 89894VB 89469466V 8945VBZZ~C G588G74G74VB-~ 588964VC 7846565F55Z-~ 95565V~BCB 557VBC~ 5576VCC 766VB~BZWC 5575956ZWBWB~BZBWCW 5756VBZZZZZZZZ-~C~X

Dictionary.` The gopher digs tunnels. When hungry, it uses them to eat plants. A dictionary could map the gopher's tunnels to its food sources.`The Dictionary` is a fast way to remember things. A key (like a string) maps to a value (like an int). We call Add() to insert keys and map them to values.`An example.` Here we add 4 keys with values to a Dictionary. Then we look inside it with the Visual Studio debugger. The Dictionary has pairs of keys and values. `String, int: `Dictionary is used with different elements. We specify its key type and its value type (string, int).`Output: `The example program has no output. It does nothing useful. At least it does not crash.`Debugger.` Let us peek inside the Visual Studio debugger to examine the memory. The Dictionary instance is represented by a collection of key and value pairs. `It is fun (and perhaps helpful) to open and close the data structure elements. Learning often involves experimentation.`The pairs like (dog, 1) are the string representations of KeyValuePair instances. KeyValuePair is a struct.`ContainsKey.` This sees if a given string is present in a Dictionary. We use string keys here—we look at more types of Dictionaries further on. ContainsKey returns true if the key is found. `ContainsKey `containskey`TryGetValue.` This is often the most efficient look up method. As the name TryGetValue implies, it tests for the key. It then returns the value if it finds the key. `TryGetValue `trygetvalue`TryGetValue, syntax 2.` We can declare the "out" variable directly in the method call. This can make the code easier to read—the statements are combined, but have the same effects. `KeyValuePair.` When a collection that implements IDictionary (like Dictionary) is used in a foreach-loop, it returns an enumeration. A Dictionary will return KeyValuePair structs in a loop. `KeyValuePair `keyvaluepair`KeyNotFoundException.` This error happens when we access a nonexistent key. With Dictionary we must test keys for existence first, with ContainsKey or TryGetValue. `KeyNotFoundException `keynotfoundexception`Loop.` Here we loop over KeyValuePairs in a Dictionary. With collections like Dictionary, we must always know the value types. With each KeyValuePair, there is a Key member and Value member. `Foreach `foreach`String, int: `The code creates a Dictionary with string keys and int values. The Dictionary stores animal counts.`In the foreach-loop, each KeyValuePair has two members, a string Key and an int Value.`Var keyword.` The final loop in the above code uses var. This reduces the amount of typing required. And it may make code easier to read for humans (like us). `Var: Dictionary `var`Keys.` Here we use the Keys property. We then look through each key and look up the values. This method is slower but has the same results. `KeyCollection: `The Keys property returns a collection of type KeyCollection, not an actual List. We can convert it into a List.`Foreach, benchmark.` Here we compare loops. A foreach-loop on KeyValuePairs is faster than the looping over Keys and accessing values in the loop body. `When possible, loop over the pairs in a Dictionary directly. Eliminating lookups will improve performance.`Sort.` A Dictionary cannot be directly sorted. But we can take its Keys and then sort those in a separate List collection. A query expression may also be used. `Sort Dictionary `sort-dictionary`Types.` Dictionary is a generic class. To use it, we must specify a type. This is a good feature. It means we can use an int key just as easily as a string key. `Int: `In this program, we see an example of a Dictionary with int keys. The values can also be any type.`LINQ.` Extension methods can be used with Dictionary. We use the ToDictionary method. This is an extension method on IEnumerable. It places keys and values into a new Dictionary. `Lambda: `The program uses lambda expressions. With these small functions, we specify a method directly as an argument.`Lambdas `lambda`The example uses ToDictionary, from System.Linq, on a string array. It creates a lookup table for the strings.`ToDictionary `todictionary`ContainsValue.` This method lacks the constant-time look up speed of ContainsKey. It instead searches the entire collection. It is linear in complexity. `ContainsValue `containsvalue`This example will loop through all elements in the Dictionary until it finds a match, or there are no more elements to check.`Speed: `MSDN states that "this method is an O(N) operation, where N is Count." It does not run in constant time.`Indexer.` We do not need to use Add to insert into a Dictionary. We can instead use the indexer, with the "[" and "]" brackets. This syntax also gets the value at the key. `Caution: `If we try to get a value at a key that doesn't exist, an exception is thrown.`With the indexer, an exception is not thrown when we assign to a key that already has a value. But with Add, an exception is thrown.`Clear.` We can erase all pairs with the Clear method. Or we can assign the Dictionary variable to null. This causes little difference in memory usage—the entries are garbage-collected. `Clear `clear`Internally: `We find that Clear calls Array.Clear, which is not managed code. Dictionary is implemented with arrays.`Array.Clear `array-clear`Count.` This computes the total number of keys in a Dictionary. This is simpler than accessing the Keys property, or looping over the Dictionary to count it. `Count `count-dictionary`Remove.` We can eliminate an entry, not just by setting its value to null or string.Empty, but by also removing the key itself. With Remove, no remnants of the key-value pair are kept. `Running the code in Visual Studio, no exceptions are thrown. When we remove a key that doesn't exist, nothing happens.`However: `Remove() throws System.ArgumentNullException with a null parameter. We cannot remove null.`Copy.` Dictionary provides a constructor that copies all values and keys into a new Dictionary instance. This constructor improves code reuse. It makes copying simpler. `Copy Dictionary `copy-dictionary`Return.` A Dictionary can be returned, or passed as an argument. 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. The same principle applies when returning values.`Return `return`List versus Dictionary.` I suggest almost always using Dictionary for lookups. In large collections, a List will become unusable for lookups. `But: `A Dictionary will still work well with large amounts of data. With Dictionary a program recovers from pathological, edge cases.`List `list`Looping: `It is faster to loop through elements in a List. If looping through elements is the most common operation, a List is superior.`Dictionary vs. List Loops `dictionary-list-loop`Compare types.` Sometimes, an array or List can be used instead of a Dictionary. This influences performance. Any analysis depends on the program. `Array vs. Dictionary `array-dictionary-test`List vs. Dictionary `dictionary-time`Composite keys.` We can sometimes use multiple variables in a key. We can create a special function that transforms those variables into a string, serializing them. `We can use the string "1,2" to mean the ints 1 and 2. This approach is not optimally fast.`Note 2: `This is similar to how composite names in programming languages use a period: "Type.Member".`Field.` Sometimes it is useful to have a Dictionary at the class level, as a field. And if we have a static class, we should initialize the Dictionary at the class level. `Avoid the static constructor—static constructors often carry performance penalties.`GetEnumerator.` With this method we can loop over a Dictionary with foreach. We call GetEnumerator() and then use a while-loop on the MoveNext method. `GetEnumerator `dictionary-getenumerator`IEqualityComparer.` Dictionary uses an IEqualityComparer to compute the hash code for its keys. We can implement this interface with a class. This can improve performance. `IEqualityComparer `iequalitycomparer`Optimization.` The Dictionary is well-optimized by the .NET Framework developers. They are smart people. But there are still techniques that influence performance. `Optimization: Dictionary `optimization`Binary format.` In serialization we write a Dictionary to the disk. Usually strings are less efficient than a binary format. But with binary we cannot easily read the file. `Dictionary Binary File `dictionary-binary`Equals.` Are two dictionaries equal? Do they have the same keys and values? With a special method we can check for equivalent data. `Dictionary Equals `dictionary-equals`Stop words.` With a Dictionary we can remove certain words from a string. Small words like "and" are sometimes called "stop words." We can use a Dictionary to help eliminate these. `Stopword Dictionary `stopword-dictionary`Combine keys.` In this example we combine the keys of two dictionaries into another collection. A HashSet is used to union the two sets of keys. `Combine Dictionary Keys `combine-keys`Map.` The Dictionary is a map. A real map helps us find our destination, but a digital map directs us from a key to a value. In languages we often find a map type. `Map `map`Case-insensitive keys.` With a special StringComparer we can have case-insensitive key lookups on a dictionary. This reduces the need to call ToLower. It reduces string creation. `Case, Dictionary `case-insensitive-dictionary`Research.` A Dictionary is powerful, but it is not magic. It just uses a good algorithm. This requires a hash code. We take keys and use arithmetic to transform them into numbers. `Locations: `We then store them in locations based on those numbers in the Dictionary. We select a "bucket" based on the hash.`Finally: `When we go to look up a key, we compute a new hash code and look for it in the Dictionary. Less searching is needed.`Quote: `We try to reference items in a table directly by doing arithmetic operations to transform keys into table addresses (Algorithms in C++ Third Edition).`It is good` that fast lookups are important. We just spent lots of time explaining how to do them. We looped over a Dictionary with KeyValuePair.`A review.` We checked key existence with ContainsKey and TryGetValue. Dictionary, like all hash tables, is fascinating. It is fast. It is useful in many places.

XYZ386:DQ JA%-Pz-yq-Pzb-v-v-v-vDQ JA%-Pz-yq-Pzb-v-v{-:Pm-:ihy-'h{:Pm-:'DQ JA%-PPhyq-PPbhvhv{XPmhX{'{hmhX{'{ JA%hyq-PPhvfhvf{PXmhXPQ'fDQ JA%{F--Pzyq-Pz{W@@XPzp'X{j-@p'XDQ JA%-Pzyq-Pz{pooPnyqoP{Wn@Ppn'DQ !JA%yq-Pzyyyiyiy{@-y,`wiy@pX3{@hy,`wiy@p3'#'#-@@DQ JA%{-i-zPyq-zPvv{i-m:'DQ DQbJA%PyqPy-@p'XDQ JA%-Pzyq-Pzvvm:X'{DQ JA%-zz-yq-zz{-y-y-y{{u98'-'-DQ JA%-Pzyq-Pzvvb{{DQ JA%FyqF'XJF-zzyq-zzBiXK{F YY; Y YY { XYX{ XXZDYZ<Y, Y> dYYYDY<Y, Y>()YXXdY.YZ"cat"Z, 2); XXdY.YZ"dog"Z, 1); XXdY.YZ"llama"Z, 0); XXdY.YZ"iguana"Z, -1); X} }Z YY; Y YY { XYX{ XXDY<Y, Y> dYYYDY<Y, Y>()YXXdY.YZ"apple"Z, 1); XXdY.YZ"windows"Z, 5);Z XXYSee whether DY cY this Y. XXZYdY.ZCYKeyZ(Z"apple"Z)) XX{ XXXYYYdY[Z"apple"Z]; XXXYY); XX}Z XXYSee whether it cY this Y. XXZY!dY.ZCYKeyZ(Z"acorn"Z)) XX{ XXXYfalse); XX} X} } Z 1 FalseZ YY; Y YY { XYX{ XXDY<Y, Y> YsYYDY<Y, Y>()YXXYs.YZ"cat"Z, "feline"); XXYs.YZ"dog"Z, "canine");Z XXYUse TryGetY. XXZY test; XXYYs.ZTryGetYZ(Z"cat"Z, out test))Z YReturns true. XXZ{ XXXYtest);Z YThis is the Y at cat. XXZ} XXYYs.ZTryGetYZ(Z"bird"Z, out test))Z YReturns false. XXZ{ XXXYfalse);Z YNot reached. XXZ} X} } Z felineZ Y YY { XYX{ XXvar YsYYDY<Y, Y>(); XXYs.YZ"A"Z, "upperYletter A"); XXYs.YZ"c"Z, "lowerYletter C");Z XXYUse inline "out Y" with TryGetY. XXZYYs.ZTryGetYZ(Z"c"Z, Zout YZ description)) XX{ XXXY.Ydescription); XX} X} } Z lowerYletter CZ YY; Y YY { XYX{Z XXYEY DY again. XXZDY<Y, Y>Z dYYDY<Y, Y>() XX{ XXX{Z"cat"Z, 2}, XXX{Z"dog"Z, 1}, XXX{Z"llama"Z, 0}, XXX{Z"iguana"Z, -1} XX};Z XXYYover pairs with Y. XXZYZ (KeyYPair<Y, Y> pairYd) XX{ XXXYZ"{0}, {1}"Z, pair.Key, pair.Y); XX}Z XXYUse var keywordYenumerate dY. XXZYZ (var pairYd) XX{ XXXYZ"{0}, {1}"Z, pair.Key, pair.Y); XX} X} } Z cat, 2 dog, 1 llama, 0 iguana, -1 cat, 2 dog, 1 llama, 0 iguana, -1Z YY; Y YY { XYX{ XXZDY<Y, Y>Z dYYDY<Y, Y>() XX{ XXX{Z"cat"Z, 2}, XXX{Z"dog"Z, 1}, XXX{Z"llama"Z, 0}, XXX{Z"iguana"Z, -1} XX};Z XXYStore keysYa Y. XXZY<Y> YYYY<Y>(d.ZKeysZ);Z XXYYthrough Y. XXZY (Y kYY) XX{ XXXYZ"{0}, {1}"Z, k, d[k]); XX} X} } Z cat, 2 dog, 1 llama, 0 iguana, -1Z YY; YY YY { XYX{ XXvar testYYZDYZ<Y, Y>(); XXtest[Z"bird"Z]Y10; XXtest[Z"frog"Z]Y20; XXtest[Z"cat"Z]Y60; XXYsumY0; XXconst Y_maxY1000000;Z XXYVersion 1: use Y loop directly on DY. XXZvar s1YY.YNew(); XXY(YiY0; i < _max; i++) XX{ XXXZYZ (var pairYtest) XXX{ XXXXsum += pair.Y; XXX} XX} XXs1Y;Z XXYVersion 2: use Y loop on Keys, then access Ys. XXZvar s2YY.YNew(); XXY(YiY0; i < _max; i++) XX{ XXXZYZ (var keyYtest.Keys) XXX{ XXXXsum += test[key]; XXX} XX} XXs2Y; XXYs1.Y); XXYs2.Y); X} } ZResultsZ Z28.117 msZXDY Y (measured January 2017) Z83.3468 msZXKeys YX (measured January 2017)Z YY; Y YY { XYX{Z XXYUse a DY with an Ykey. XXZDY<Y, Y> dictYYZDYZ<Y, Y>(); XXdict.YZ100Z, "Bill"); XXdict.YZ200Z, "Steve");Z XXYWe can look up the Yin the DY. XXZYdict.CYKey(Z200Z)) XX{ XXXYtrue); XX} X} } Z TrueZ YY; YYY.LinqYYY { XYX{ XXY[] arrYYY[] XX{ XXXZ"One"Z, XXXZ"Two"Z XX}; XXvar dictYarr.ZToDYZ(item => item, item => true); XXY (var pairYdict) XX{ XXXY"{0}, {1}", XXXXpair.Key, XXXXpair.Y); XX} X} } Z One, True Two, TrueZ YY; Y YY { XYX{ XXDY<Y, Y> dYYDY<Y, Y>(); XXd.Y"cat", Z1Z); XXd.Y"dog", Z2Z); XXYd.ZCYYZ(1)) XX{ XXXYtrue);Z YTrue. XXZ} X} } Z TrueZ YY; Y YY { XYX{ XXZDYZ<Y, Y> dYYYDY<Y, Y>();Z XXYWe can assign with the indexer. XXZdY[1]Y2; XXdY[2]Y1; XXdY[1]Y3;Z YReassign. XXYY with the indexer. XXYAn exception occurs if no Y exists. XXZYdY[1]); XXYdY[2]); X} } Z 3 1Z YY; Y YY { XYX{ XXDY<Y, Y> dYYDY<Y, Y>(); XXd.YZ"cat"Z, 1); XXd.YZ"dog"Z, 2)YXXd.ZRemoveZ(Z"cat"Z);Z YRemoves cat. XXZd.ZRemoveZ(Z"nothing"Z);Z YDoesn't remove anything. XZ} }Z YY; Y YY { XYX{ XXEY eYYEY(); XXYe.ZGetYZ()); X} } YEY { XZDYZ<Y, Y> _dYYDY<Y, Y>() X{ XX{1, 1}, XX{2, 3}, XX{3, 5}, XX{6, 10} X}; XYYZGetYZ() X{ XXY _d[2];Z YEY only. XZ} } Z 3Z

-Z[[ge)@XT]bV`Add methodContainsKeyTryGetValueinline out, TryGetValueforeach on Dictionarygets Keysbenchmarks foreach on Dictionaryint keysLINQContainsValueDictionary indexerRemoveDictionary field