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

Dictionary.` In a city many buildings are found. To find a structure we can search the entire city, street by street. But this becomes slow.`With a dictionary,` we can map a building to a location. We can map "park" to where it is found. Then to find the park, we can skip searching the whole city.`An example.` 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.

IHYXQYYHDHQ;YH YHJHAY{YIH%I{YIIQDH-Q<HP, Hz> dH-HyHqDH-<HP, Hz>()HbIIdH-.HvQXcatXQ, 2);YIIdH-.HvQXdogXQ, 1);YIIdH-.HvQXllamaXQ, 0);YIIdH-.HvQXiguanaXQ, -1);YI}Y}QYYHDHQ;YH YHJHAY{YIH%I{YIIDH-<HP, Hz> dH-HyHqDH-<HP, Hz>()HbIIdH-.HvQXappleXQ, 1);YIIdH-.HvQXwindowsXQ, 5);QYYIIH{See whether DH- cH: this HP.YIIQHmdH-.QCH:KeyQ(QXappleXQ))YII{YIIIHiHhHydH-[QXappleXQ];YIIIH'Hh);YII}QYYIIH{See whether it cH: this HP.YIIQHm!dH-.QCH:KeyQ(QXacornXQ))YII{YIIIH'false);YII}YI}Y}YYQYY1YFalseQYYHDHQ;YH YHJHAY{YIH%I{YIIDH-<HP, HP> HhsHyHqDH-<HP, HP>()HbIIHhs.HvQXcatXQ, XfelineX);YIIHhs.HvQXdogXQ, XcanineX);QYIIH{Use TryGetHX.YIIQHP test;YIIHmHhs.QTryGetHXQ(QXcatXQ, out test))Q H{Returns true.YIIQ{YIIIH'test);Q H{This is the Hh at cat.YIIQ}YIIHmHhs.QTryGetHXQ(QXbirdXQ, out test))Q H{Returns false.YIIQ{YIIIH'false);Q H{Not reached.YIIQ}YI}Y}YYQYYfelineQYYH YHJHAY{YIH%I{YIIvar HhsHyHqDH-<HP, HP>();YIIHhs.HvQXAXQ, XupperHfletter AX);YIIHhs.HvQXcXQ, XlowerHfletter CX);QYYIIH{Use inline Xout HPX with TryGetHX.YIIQHmHhs.QTryGetHXQ(QXcXQ, Qout HPQ description))YII{YIIIHQ.H'description);YII}YI}Y}YYQYYlowerHfletter CQYYHDHQ;YH YHJHAY{YIH%I{QYIIH{EHF DH- again.YIIQDH-<HP, Hz>Q dHyHqDH-<HP, Hz>()YII{YIII{QXcatXQ, 2},YIII{QXdogXQ, 1},YIII{QXllamaXQ, 0},YIII{QXiguanaXQ, -1}YII};QYIIH{HWover pairs with H@.YIIQH@Q (KeyHXPair<HP, Hz> pairHpd)YII{YIIIH'QX{0}, {1}XQ, pair.Key, pair.HX);YII}QYIIH{Use var keywordHjenumerate dH-.YIIQH@Q (var pairHpd)YII{YIIIH'QX{0}, {1}XQ, pair.Key, pair.HX);YII}YI}Y}YYQYYcat, 2Ydog, 1Yllama, 0Yiguana, -1YYcat, 2Ydog, 1Yllama, 0Yiguana, -1QYYHDHQ;YH YHJHAY{YIH%I{YIIQDH-<HP, Hz>Q dHyHqDH-<HP, Hz>()YII{YIII{QXcatXQ, 2},YIII{QXdogXQ, 1},YIII{QXllamaXQ, 0},YIII{QXiguanaXQ, -1}YII};QYIIH{Store keysHpa Ho.YIIQHo<HP> HnHyHqHo<HP>(d.QKeysQ);QYIIH{HWthrough Hn.YIIQH@ (HP kHpHn)YII{YIIIH'QX{0}, {1}XQ, k, d[k]);YII}YI}Y}YYQYYcat, 2Ydog, 1Yllama, 0Yiguana, -1QYYHDHQ;YH H!YHJHAY{YIH%I{YIIvar testHyHqQDH-Q<HP, Hz>();YIItest[QXbirdXQ]Hy10;YIItest[QXfrogXQ]Hy20;YIItest[QXcatXQ]Hy60;YIIHisumHy0;YIIconst Hi_maxHy1000000;QYYIIH{Version 1: use H@ loop directly on DH-.YIIQvar s1HyH,.H`New();YIIHw(HiiHy0; i < _max; i++)YII{YIIIQH@Q (var pairHptest)YIII{YIIIIsum += pair.HX;YIII}YII}YIIs1H3;QYYIIH{Version 2: use H@ loop on Keys, then access Hhs.YIIQvar s2HyH,.H`New();YIIHw(HiiHy0; i < _max; i++)YII{YIIIQH@Q (var keyHptest.Keys)YIII{YIIIIsum += test[key];YIII}YII}YIIs2H3;YIIH's1.H#);YIIH's2.H#);YI}Y}YYQResultsQYYQ28.117 msQIDH- H@ (measured January 2017)YQ83.3468 msQIKeys H@I (measured January 2017)QYYHDHQ;YH YHJHAY{YIH%I{QYIIH{Use a DH- with an Hikey.YIIQDH-<Hz, HP> dictHyHqQDH-Q<Hz, HP>();YIIdict.HvQ100Q, XBillX);YIIdict.HvQ200Q, XSteveX);QYIIH{We can look up the Hiin the DH-.YIIQHmdict.CH:Key(Q200Q))YII{YIIIH'true);YII}YI}Y}YYQYYTrueQYYHDHQ;YH HDHQ.LinqHbHJHAY{YIH%I{YIIHP[] arrHyHqHP[]YII{YIIIQXOneXQ,YIIIQXTwoXQYII};YIIvar dictHyarr.QToDH-Q(item => item, item => true);YIIH@ (var pairHpdict)YII{YIIIH'X{0}, {1}X,YIIIIpair.Key,YIIIIpair.HX);YII}YI}Y}YYQYYOne, TrueYTwo, TrueQYYHDHQ;YH YHJHAY{YIH%I{YIIDH-<HP, Hz> dHyHqDH-<HP, Hz>();YIId.HvXcatX, Q1Q);YIId.HvXdogX, Q2Q);YIIHmd.QCH:HXQ(1))YII{YIIIH'true);Q H{True.YIIQ}YI}Y}YYQYYTrueQYYHDHQ;YH YHJHAY{YIH%I{YIIQDH-Q<Hz, Hz> dH-HyHqDH-<Hz, Hz>();QYYIIH{We can assign with the indexer.YIIQdH-[1]Hy2;YIIdH-[2]Hy1;YIIdH-[1]Hy3;Q H{Reassign.YYIIH{Hu with the indexer.YIIH9An exception occurs if no H8 exists.YIIQH'dH-[1]);YIIH'dH-[2]);YI}Y}YYQYY3Y1QYYHDHQ;YH YHJHAY{YIH%I{YIIDH-<HP, Hz> dHyHqDH-<HP, Hz>();YIId.HvQXcatXQ, 1);YIId.HvQXdogXQ, 2)HbIId.QRemoveQ(QXcatXQ);Q H{Removes cat.YIIQd.QRemoveQ(QXnothingXQ);Q H{Doesn't remove anything.YIQ}Y}QYYHDHQ;YH YHJHAY{YIH%I{YIIEHF eHyHqEHF();YIIH'e.QGetHXQ());YI}Y}YYHJEHFY{YIQDH-Q<Hz, Hz> _dHyHqDH-<Hz, Hz>()YI{YII{1, 1},YII{2, 3},YII{3, 5},YII{6, 10}YI};YIHBHiQGetHXQ()YI{YIIHK _d[2];Q H{EHF only.YIQ}Y}YYQYY3Q

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