2 // System.Collections.Generic.Dictionary
5 // Sureshkumar T (tsureshkumar@novell.com)
6 // Marek Safar (marek.safar@seznam.cz) (stubs)
7 // Ankit Jain (radical@corewars.org)
8 // David Waite (mass@akuma.org)
9 // Juraj Skripsky (js@hotfeet.ch)
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Copyright (C) 2005 David Waite
14 // Copyright (C) 2007 HotFeet GmbH (http://www.hotfeet.ch)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 using System.Collections;
40 using System.Collections.Generic;
41 using System.Runtime.Serialization;
42 using System.Security.Permissions;
43 using System.Runtime.InteropServices;
45 namespace System.Collections.Generic {
48 * Declare this outside the main class so it doesn't have to be inflated for each
49 * instantiation of Dictionary.
51 internal struct Link {
58 public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>,
61 ICollection<KeyValuePair<TKey, TValue>>,
62 IEnumerable<KeyValuePair<TKey, TValue>>,
64 IDeserializationCallback
66 // The implementation of this class uses a hash table and linked lists
67 // (see: http://msdn2.microsoft.com/en-us/library/ms379571(VS.80).aspx).
69 // We use a kind of "mini-heap" instead of reference-based linked lists:
70 // "keySlots" and "valueSlots" is the heap itself, it stores the data
71 // "linkSlots" contains information about how the slots in the heap
72 // are connected into linked lists
73 // In addition, the HashCode field can be used to check if the
74 // corresponding key and value are present (HashCode has the
75 // HASH_FLAG bit set in this case), so, to iterate over all the
76 // items in the dictionary, simply iterate the linkSlots array
77 // and check for the HASH_FLAG bit in the HashCode field.
78 // For this reason, each time a hashcode is calculated, it needs
79 // to be ORed with HASH_FLAG before comparing it with the save hashcode.
80 // "touchedSlots" and "emptySlot" manage the free space in the heap
82 const int INITIAL_SIZE = 10;
83 const float DEFAULT_LOAD_FACTOR = (90f / 100);
84 const int NO_SLOT = -1;
85 const int HASH_FLAG = -2147483648;
87 // The hash table contains indices into the linkSlots array
90 // All (key,value) pairs are chained into linked lists. The connection
91 // information is stored in "linkSlots" along with the key's hash code
92 // (for performance reasons).
93 // TODO: get rid of the hash code in Link (this depends on a few
94 // JIT-compiler optimizations)
95 // Every link in "linkSlots" corresponds to the (key,value) pair
96 // in "keySlots"/"valueSlots" with the same index.
101 // The number of slots in "linkSlots" and "keySlots"/"valueSlots" that
102 // are in use (i.e. filled with data) or have been used and marked as
106 // The index of the first slot in the "empty slots chain".
107 // "Remove()" prepends the cleared slots to the empty chain.
108 // "Add()" fills the first slot in the empty slots chain with the
109 // added item (or increases "touchedSlots" if the chain itself is empty).
112 // The number of (key,value) pairs in this dictionary.
115 // The number of (key,value) pairs the dictionary can hold without
116 // resizing the hash table and the slots arrays.
119 IEqualityComparer<TKey> hcp;
120 SerializationInfo serialization_info;
122 // The number of changes made to this dictionary. Used by enumerators
123 // to detect changes and invalidate themselves.
127 get { return count; }
130 public TValue this [TKey key] {
133 throw new ArgumentNullException ("key");
135 // get first item of linked list corresponding to given key
136 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
137 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
139 // walk linked list until right slot is found or end is reached
140 while (cur != NO_SLOT) {
141 // The ordering is important for compatibility with MS and strange
142 // Object.Equals () implementations
143 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
144 return valueSlots [cur];
145 cur = linkSlots [cur].Next;
147 throw new KeyNotFoundException ();
152 throw new ArgumentNullException ("key");
154 // get first item of linked list corresponding to given key
155 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
156 int index = (hashCode & int.MaxValue) % table.Length;
157 int cur = table [index] - 1;
159 // walk linked list until right slot (and its predecessor) is
160 // found or end is reached
162 if (cur != NO_SLOT) {
164 // The ordering is important for compatibility with MS and strange
165 // Object.Equals () implementations
166 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
169 cur = linkSlots [cur].Next;
170 } while (cur != NO_SLOT);
173 // is there no slot for the given key yet?
174 if (cur == NO_SLOT) {
175 // there is no existing slot for the given key,
176 // allocate one and prepend it to its corresponding linked
179 if (++count > threshold) {
181 index = (hashCode & int.MaxValue) % table.Length;
184 // find an empty slot
187 cur = touchedSlots++;
189 emptySlot = linkSlots [cur].Next;
191 // prepend the added item to its linked list,
192 // update the hash table
193 linkSlots [cur].Next = table [index] - 1;
194 table [index] = cur + 1;
196 // store the new item and its hash code
197 linkSlots [cur].HashCode = hashCode;
198 keySlots [cur] = key;
200 // we already have a slot for the given key,
201 // update the existing slot
203 // if the slot is not at the front of its linked list,
205 if (prev != NO_SLOT) {
206 linkSlots [prev].Next = linkSlots [cur].Next;
207 linkSlots [cur].Next = table [index] - 1;
208 table [index] = cur + 1;
212 // store the item's data itself
213 valueSlots [cur] = value;
221 Init (INITIAL_SIZE, null);
224 public Dictionary (IEqualityComparer<TKey> comparer)
226 Init (INITIAL_SIZE, comparer);
229 public Dictionary (IDictionary<TKey, TValue> dictionary)
230 : this (dictionary, null)
234 public Dictionary (int capacity)
236 Init (capacity, null);
239 public Dictionary (IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
241 if (dictionary == null)
242 throw new ArgumentNullException ("dictionary");
243 int capacity = dictionary.Count;
244 Init (capacity, comparer);
245 foreach (KeyValuePair<TKey, TValue> entry in dictionary)
246 this.Add (entry.Key, entry.Value);
249 public Dictionary (int capacity, IEqualityComparer<TKey> comparer)
251 Init (capacity, comparer);
254 protected Dictionary (SerializationInfo info, StreamingContext context)
256 serialization_info = info;
259 private void Init (int capacity, IEqualityComparer<TKey> hcp)
262 throw new ArgumentOutOfRangeException ("capacity");
263 this.hcp = (hcp != null) ? hcp : EqualityComparer<TKey>.Default;
265 capacity = INITIAL_SIZE;
267 /* Modify capacity so 'capacity' elements can be added without resizing */
268 capacity = (int)(capacity / DEFAULT_LOAD_FACTOR) + 1;
270 InitArrays (capacity);
274 private void InitArrays (int size) {
275 table = new int [size];
277 linkSlots = new Link [size];
280 keySlots = new TKey [size];
281 valueSlots = new TValue [size];
284 threshold = (int)(table.Length * DEFAULT_LOAD_FACTOR);
285 if (threshold == 0 && table.Length > 0)
289 void CopyToCheck (Array array, int index)
292 throw new ArgumentNullException ("array");
294 throw new ArgumentOutOfRangeException ("index");
295 // we want no exception for index==array.Length && Count == 0
296 if (index > array.Length)
297 throw new ArgumentException ("index larger than largest valid index of array");
298 if (array.Length - index < Count)
299 throw new ArgumentException ("Destination array cannot hold the requested elements!");
302 delegate TRet Transform<TRet> (TKey key, TValue value);
304 void Do_CopyTo<TRet, TElem> (TElem [] array, int index, Transform<TRet> transform)
307 for (int i = 0; i < touchedSlots; i++) {
308 if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
309 array [index++] = transform (keySlots [i], valueSlots [i]);
313 static KeyValuePair<TKey, TValue> make_pair (TKey key, TValue value)
315 return new KeyValuePair<TKey, TValue> (key, value);
318 static TKey pick_key (TKey key, TValue value)
323 static TValue pick_value (TKey key, TValue value)
328 void CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
330 CopyToCheck (array, index);
331 Do_CopyTo<KeyValuePair<TKey, TValue>, KeyValuePair<TKey, TValue>> (array, index, make_pair);
334 void Do_ICollectionCopyTo<TRet> (Array array, int index, Transform<TRet> transform)
336 Type src = typeof (TRet);
337 Type tgt = array.GetType ().GetElementType ();
340 if ((src.IsPrimitive || tgt.IsPrimitive) && !tgt.IsAssignableFrom (src))
341 throw new Exception (); // we don't care. it'll get transformed to an ArgumentException below
343 Do_CopyTo <TRet, TRet> ((TRet []) array, index, transform);
344 } catch (Exception e) {
345 throw new ArgumentException ("Cannot copy source collection elements to destination array", "array", e);
349 private void Resize ()
351 // From the SDK docs:
352 // Hashtable is automatically increased
353 // to the smallest prime number that is larger
354 // than twice the current number of Hashtable buckets
355 int newSize = Hashtable.ToPrime ((table.Length << 1) | 1);
357 // allocate new hash table and link slots array
358 int [] newTable = new int [newSize];
359 Link [] newLinkSlots = new Link [newSize];
361 for (int i = 0; i < table.Length; i++) {
362 int cur = table [i] - 1;
363 while (cur != NO_SLOT) {
364 int hashCode = newLinkSlots [cur].HashCode = hcp.GetHashCode(keySlots [cur]) | HASH_FLAG;
365 int index = (hashCode & int.MaxValue) % newSize;
366 newLinkSlots [cur].Next = newTable [index] - 1;
367 newTable [index] = cur + 1;
368 cur = linkSlots [cur].Next;
372 linkSlots = newLinkSlots;
374 // allocate new data slots, copy data
375 TKey [] newKeySlots = new TKey [newSize];
376 TValue [] newValueSlots = new TValue [newSize];
377 Array.Copy (keySlots, 0, newKeySlots, 0, touchedSlots);
378 Array.Copy (valueSlots, 0, newValueSlots, 0, touchedSlots);
379 keySlots = newKeySlots;
380 valueSlots = newValueSlots;
382 threshold = (int)(newSize * DEFAULT_LOAD_FACTOR);
385 public void Add (TKey key, TValue value)
388 throw new ArgumentNullException ("key");
390 // get first item of linked list corresponding to given key
391 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
392 int index = (hashCode & int.MaxValue) % table.Length;
393 int cur = table [index] - 1;
395 // walk linked list until end is reached (throw an exception if a
396 // existing slot is found having an equivalent key)
397 while (cur != NO_SLOT) {
398 // The ordering is important for compatibility with MS and strange
399 // Object.Equals () implementations
400 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
401 throw new ArgumentException ("An element with the same key already exists in the dictionary.");
402 cur = linkSlots [cur].Next;
405 if (++count > threshold) {
407 index = (hashCode & int.MaxValue) % table.Length;
410 // find an empty slot
413 cur = touchedSlots++;
415 emptySlot = linkSlots [cur].Next;
417 // store the hash code of the added item,
418 // prepend the added item to its linked list,
419 // update the hash table
420 linkSlots [cur].HashCode = hashCode;
421 linkSlots [cur].Next = table [index] - 1;
422 table [index] = cur + 1;
425 keySlots [cur] = key;
426 valueSlots [cur] = value;
431 public IEqualityComparer<TKey> Comparer {
438 // clear the hash table
439 Array.Clear (table, 0, table.Length);
441 Array.Clear (keySlots, 0, keySlots.Length);
442 Array.Clear (valueSlots, 0, valueSlots.Length);
443 Array.Clear (linkSlots, 0, linkSlots.Length);
445 // empty the "empty slots chain"
452 public bool ContainsKey (TKey key)
455 throw new ArgumentNullException ("key");
457 // get first item of linked list corresponding to given key
458 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
459 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
461 // walk linked list until right slot is found or end is reached
462 while (cur != NO_SLOT) {
463 // The ordering is important for compatibility with MS and strange
464 // Object.Equals () implementations
465 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
467 cur = linkSlots [cur].Next;
473 public bool ContainsValue (TValue value)
475 IEqualityComparer<TValue> cmp = EqualityComparer<TValue>.Default;
477 for (int i = 0; i < table.Length; i++) {
478 int cur = table [i] - 1;
479 while (cur != NO_SLOT) {
480 if (cmp.Equals (valueSlots [cur], value))
482 cur = linkSlots [cur].Next;
488 [SecurityPermission (SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
489 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
492 throw new ArgumentNullException ("info");
494 info.AddValue ("Version", generation);
495 info.AddValue ("Comparer", hcp);
496 KeyValuePair<TKey, TValue> [] data = null;
498 data = new KeyValuePair<TKey,TValue> [count];
501 info.AddValue ("HashSize", table.Length);
502 info.AddValue ("KeyValuePairs", data);
505 public virtual void OnDeserialization (object sender)
507 if (serialization_info == null)
510 generation = serialization_info.GetInt32 ("Version");
511 hcp = (IEqualityComparer<TKey>) serialization_info.GetValue ("Comparer", typeof (IEqualityComparer<TKey>));
513 int hashSize = serialization_info.GetInt32 ("HashSize");
514 KeyValuePair<TKey, TValue> [] data =
515 (KeyValuePair<TKey, TValue> [])
516 serialization_info.GetValue ("KeyValuePairs", typeof (KeyValuePair<TKey, TValue> []));
518 if (hashSize < INITIAL_SIZE)
519 hashSize = INITIAL_SIZE;
520 InitArrays (hashSize);
524 for (int i = 0; i < data.Length; ++i)
525 Add (data [i].Key, data [i].Value);
528 serialization_info = null;
531 public bool Remove (TKey key)
534 throw new ArgumentNullException ("key");
536 // get first item of linked list corresponding to given key
537 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
538 int index = (hashCode & int.MaxValue) % table.Length;
539 int cur = table [index] - 1;
541 // if there is no linked list, return false
545 // walk linked list until right slot (and its predecessor) is
546 // found or end is reached
549 // The ordering is important for compatibility with MS and strange
550 // Object.Equals () implementations
551 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
554 cur = linkSlots [cur].Next;
555 } while (cur != NO_SLOT);
557 // if we reached the end of the chain, return false
562 // remove slot from linked list
563 // is slot at beginning of linked list?
565 table [index] = linkSlots [cur].Next + 1;
567 linkSlots [prev].Next = linkSlots [cur].Next;
569 // mark slot as empty and prepend it to "empty slots chain"
570 linkSlots [cur].Next = emptySlot;
573 linkSlots [cur].HashCode = 0;
574 // clear empty key and value slots
575 keySlots [cur] = default (TKey);
576 valueSlots [cur] = default (TValue);
582 public bool TryGetValue (TKey key, out TValue value)
585 throw new ArgumentNullException ("key");
587 // get first item of linked list corresponding to given key
588 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
589 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
591 // walk linked list until right slot is found or end is reached
592 while (cur != NO_SLOT) {
593 // The ordering is important for compatibility with MS and strange
594 // Object.Equals () implementations
595 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key)) {
596 value = valueSlots [cur];
599 cur = linkSlots [cur].Next;
602 // we did not find the slot
603 value = default (TValue);
607 ICollection<TKey> IDictionary<TKey, TValue>.Keys {
611 ICollection<TValue> IDictionary<TKey, TValue>.Values {
612 get { return Values; }
615 public KeyCollection Keys {
616 get { return new KeyCollection (this); }
619 public ValueCollection Values {
620 get { return new ValueCollection (this); }
623 ICollection IDictionary.Keys {
627 ICollection IDictionary.Values {
628 get { return Values; }
631 bool IDictionary.IsFixedSize {
632 get { return false; }
635 bool IDictionary.IsReadOnly {
636 get { return false; }
639 TKey ToTKey (object key)
642 throw new ArgumentNullException ("key");
644 throw new ArgumentException ("not of type: " + typeof (TKey).ToString (), "key");
648 TValue ToTValue (object value)
650 if (value == null && !typeof (TValue).IsValueType)
651 return default (TValue);
652 if (!(value is TValue))
653 throw new ArgumentException ("not of type: " + typeof (TValue).ToString (), "value");
654 return (TValue) value;
657 object IDictionary.this [object key] {
659 if (key is TKey && ContainsKey((TKey) key))
660 return this [ToTKey (key)];
663 set { this [ToTKey (key)] = ToTValue (value); }
666 void IDictionary.Add (object key, object value)
668 this.Add (ToTKey (key), ToTValue (value));
671 bool IDictionary.Contains (object key)
674 throw new ArgumentNullException ("key");
676 return ContainsKey ((TKey) key);
680 void IDictionary.Remove (object key)
683 throw new ArgumentNullException ("key");
688 bool ICollection.IsSynchronized {
689 get { return false; }
692 object ICollection.SyncRoot {
696 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
697 get { return false; }
700 void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair)
702 Add (keyValuePair.Key, keyValuePair.Value);
705 bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair)
707 return ContainsKeyValuePair (keyValuePair);
710 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
712 this.CopyTo (array, index);
715 bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair)
717 if (!ContainsKeyValuePair (keyValuePair))
720 return Remove (keyValuePair.Key);
723 bool ContainsKeyValuePair (KeyValuePair<TKey, TValue> pair)
726 if (!TryGetValue (pair.Key, out value))
729 return EqualityComparer<TValue>.Default.Equals (pair.Value, value);
732 void ICollection.CopyTo (Array array, int index)
734 KeyValuePair<TKey, TValue> [] pairs = array as KeyValuePair<TKey, TValue> [];
736 this.CopyTo (pairs, index);
740 CopyToCheck (array, index);
741 DictionaryEntry [] entries = array as DictionaryEntry [];
742 if (entries != null) {
743 Do_CopyTo (entries, index, delegate (TKey key, TValue value) { return new DictionaryEntry (key, value); });
747 Do_ICollectionCopyTo<KeyValuePair<TKey, TValue>> (array, index, make_pair);
750 IEnumerator IEnumerable.GetEnumerator ()
752 return new Enumerator (this);
755 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
757 return new Enumerator (this);
760 IDictionaryEnumerator IDictionary.GetEnumerator ()
762 return new ShimEnumerator (this);
765 public Enumerator GetEnumerator ()
767 return new Enumerator (this);
771 private class ShimEnumerator : IDictionaryEnumerator, IEnumerator
773 Enumerator host_enumerator;
774 public ShimEnumerator (Dictionary<TKey, TValue> host)
776 host_enumerator = host.GetEnumerator ();
779 public void Dispose ()
781 host_enumerator.Dispose ();
784 public bool MoveNext ()
786 return host_enumerator.MoveNext ();
789 public DictionaryEntry Entry {
790 get { return ((IDictionaryEnumerator) host_enumerator).Entry; }
794 get { return host_enumerator.Current.Key; }
797 public object Value {
798 get { return host_enumerator.Current.Value; }
801 // This is the raison d' etre of this $%!@$%@^@ class.
802 // We want: IDictionary.GetEnumerator ().Current is DictionaryEntry
803 public object Current {
804 get { return Entry; }
809 host_enumerator.Reset ();
814 public struct Enumerator : IEnumerator<KeyValuePair<TKey,TValue>>,
815 IDisposable, IDictionaryEnumerator, IEnumerator
817 Dictionary<TKey, TValue> dictionary;
821 internal KeyValuePair<TKey, TValue> current;
823 internal Enumerator (Dictionary<TKey, TValue> dictionary)
826 this.dictionary = dictionary;
827 stamp = dictionary.generation;
830 public bool MoveNext ()
837 while (next < dictionary.touchedSlots) {
839 if ((dictionary.linkSlots [cur].HashCode & HASH_FLAG) != 0) {
840 current = new KeyValuePair <TKey, TValue> (
841 dictionary.keySlots [cur],
842 dictionary.valueSlots [cur]
852 // No error checking happens. Usually, Current is immediately preceded by a MoveNext(), so it's wasteful to check again
853 public KeyValuePair<TKey, TValue> Current {
854 get { return current; }
857 internal TKey CurrentKey {
864 internal TValue CurrentValue {
867 return current.Value;
871 object IEnumerator.Current {
878 void IEnumerator.Reset ()
883 internal void Reset ()
889 DictionaryEntry IDictionaryEnumerator.Entry {
892 return new DictionaryEntry (current.Key, current.Value);
896 object IDictionaryEnumerator.Key {
897 get { return CurrentKey; }
900 object IDictionaryEnumerator.Value {
901 get { return CurrentValue; }
906 if (dictionary == null)
907 throw new ObjectDisposedException (null);
908 if (dictionary.generation != stamp)
909 throw new InvalidOperationException ("out of sync");
912 void VerifyCurrent ()
916 throw new InvalidOperationException ("Current is not valid");
919 public void Dispose ()
925 // This collection is a read only collection
927 public sealed class KeyCollection : ICollection<TKey>, IEnumerable<TKey>, ICollection, IEnumerable {
928 Dictionary<TKey, TValue> dictionary;
930 public KeyCollection (Dictionary<TKey, TValue> dictionary)
932 if (dictionary == null)
933 throw new ArgumentNullException ("dictionary");
934 this.dictionary = dictionary;
938 public void CopyTo (TKey [] array, int index)
940 dictionary.CopyToCheck (array, index);
941 dictionary.Do_CopyTo<TKey, TKey> (array, index, pick_key);
944 public Enumerator GetEnumerator ()
946 return new Enumerator (dictionary);
949 void ICollection<TKey>.Add (TKey item)
951 throw new NotSupportedException ("this is a read-only collection");
954 void ICollection<TKey>.Clear ()
956 throw new NotSupportedException ("this is a read-only collection");
959 bool ICollection<TKey>.Contains (TKey item)
961 return dictionary.ContainsKey (item);
964 bool ICollection<TKey>.Remove (TKey item)
966 throw new NotSupportedException ("this is a read-only collection");
969 IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator ()
971 return this.GetEnumerator ();
974 void ICollection.CopyTo (Array array, int index)
976 var target = array as TKey [];
977 if (target != null) {
978 CopyTo (target, index);
982 dictionary.CopyToCheck (array, index);
983 dictionary.Do_ICollectionCopyTo<TKey> (array, index, pick_key);
986 IEnumerator IEnumerable.GetEnumerator ()
988 return this.GetEnumerator ();
992 get { return dictionary.Count; }
995 bool ICollection<TKey>.IsReadOnly {
999 bool ICollection.IsSynchronized {
1000 get { return false; }
1003 object ICollection.SyncRoot {
1004 get { return ((ICollection) dictionary).SyncRoot; }
1008 public struct Enumerator : IEnumerator<TKey>, IDisposable, IEnumerator {
1009 Dictionary<TKey, TValue>.Enumerator host_enumerator;
1011 internal Enumerator (Dictionary<TKey, TValue> host)
1013 host_enumerator = host.GetEnumerator ();
1016 public void Dispose ()
1018 host_enumerator.Dispose ();
1021 public bool MoveNext ()
1023 return host_enumerator.MoveNext ();
1026 public TKey Current {
1027 get { return host_enumerator.current.Key; }
1030 object IEnumerator.Current {
1031 get { return host_enumerator.CurrentKey; }
1034 void IEnumerator.Reset ()
1036 host_enumerator.Reset ();
1041 // This collection is a read only collection
1043 public sealed class ValueCollection : ICollection<TValue>, IEnumerable<TValue>, ICollection, IEnumerable {
1044 Dictionary<TKey, TValue> dictionary;
1046 public ValueCollection (Dictionary<TKey, TValue> dictionary)
1048 if (dictionary == null)
1049 throw new ArgumentNullException ("dictionary");
1050 this.dictionary = dictionary;
1053 public void CopyTo (TValue [] array, int index)
1055 dictionary.CopyToCheck (array, index);
1056 dictionary.Do_CopyTo<TValue, TValue> (array, index, pick_value);
1059 public Enumerator GetEnumerator ()
1061 return new Enumerator (dictionary);
1064 void ICollection<TValue>.Add (TValue item)
1066 throw new NotSupportedException ("this is a read-only collection");
1069 void ICollection<TValue>.Clear ()
1071 throw new NotSupportedException ("this is a read-only collection");
1074 bool ICollection<TValue>.Contains (TValue item)
1076 return dictionary.ContainsValue (item);
1079 bool ICollection<TValue>.Remove (TValue item)
1081 throw new NotSupportedException ("this is a read-only collection");
1084 IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator ()
1086 return this.GetEnumerator ();
1089 void ICollection.CopyTo (Array array, int index)
1091 var target = array as TValue [];
1092 if (target != null) {
1093 CopyTo (target, index);
1097 dictionary.CopyToCheck (array, index);
1098 dictionary.Do_ICollectionCopyTo<TValue> (array, index, pick_value);
1101 IEnumerator IEnumerable.GetEnumerator ()
1103 return this.GetEnumerator ();
1107 get { return dictionary.Count; }
1110 bool ICollection<TValue>.IsReadOnly {
1111 get { return true; }
1114 bool ICollection.IsSynchronized {
1115 get { return false; }
1118 object ICollection.SyncRoot {
1119 get { return ((ICollection) dictionary).SyncRoot; }
1123 public struct Enumerator : IEnumerator<TValue>, IDisposable, IEnumerator {
1124 Dictionary<TKey, TValue>.Enumerator host_enumerator;
1126 internal Enumerator (Dictionary<TKey,TValue> host)
1128 host_enumerator = host.GetEnumerator ();
1131 public void Dispose ()
1133 host_enumerator.Dispose ();
1136 public bool MoveNext ()
1138 return host_enumerator.MoveNext ();
1141 public TValue Current {
1142 get { return host_enumerator.current.Value; }
1145 object IEnumerator.Current {
1146 get { return host_enumerator.CurrentValue; }
1149 void IEnumerator.Reset ()
1151 host_enumerator.Reset ();