2009-02-04 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / class / corlib / System.Collections.Generic / Dictionary.cs
blobd400b4a0b11b6939b2ceef14a97927a73017ac79
1 //
2 // System.Collections.Generic.Dictionary
3 //
4 // Authors:
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:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
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.
36 #if NET_2_0
38 using System;
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 {
47 /*
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 {
52 public int HashCode;
53 public int Next;
56 [ComVisible(false)]
57 [Serializable]
58 public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>,
59 IDictionary,
60 ICollection,
61 ICollection<KeyValuePair<TKey, TValue>>,
62 IEnumerable<KeyValuePair<TKey, TValue>>,
63 ISerializable,
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).
68 //
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
88 int [] table;
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.
97 Link [] linkSlots;
98 TKey [] keySlots;
99 TValue [] valueSlots;
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
103 // "empty" later on.
104 int touchedSlots;
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).
110 int emptySlot;
112 // The number of (key,value) pairs in this dictionary.
113 int count;
115 // The number of (key,value) pairs the dictionary can hold without
116 // resizing the hash table and the slots arrays.
117 int threshold;
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.
124 int generation;
126 public int Count {
127 get { return count; }
130 public TValue this [TKey key] {
131 get {
132 if (key == null)
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 ();
150 set {
151 if (key == null)
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
161 int prev = NO_SLOT;
162 if (cur != NO_SLOT) {
163 do {
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))
167 break;
168 prev = cur;
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
177 // list
179 if (++count > threshold) {
180 Resize ();
181 index = (hashCode & int.MaxValue) % table.Length;
184 // find an empty slot
185 cur = emptySlot;
186 if (cur == NO_SLOT)
187 cur = touchedSlots++;
188 else
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;
199 } else {
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,
204 // we move it there
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;
215 generation++;
219 public Dictionary ()
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)
261 if (capacity < 0)
262 throw new ArgumentOutOfRangeException ("capacity");
263 this.hcp = (hcp != null) ? hcp : EqualityComparer<TKey>.Default;
264 if (capacity == 0)
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);
271 generation = 0;
274 private void InitArrays (int size) {
275 table = new int [size];
277 linkSlots = new Link [size];
278 emptySlot = NO_SLOT;
280 keySlots = new TKey [size];
281 valueSlots = new TValue [size];
282 touchedSlots = 0;
284 threshold = (int)(table.Length * DEFAULT_LOAD_FACTOR);
285 if (threshold == 0 && table.Length > 0)
286 threshold = 1;
289 void CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
291 if (array == null)
292 throw new ArgumentNullException ("array");
293 if (index < 0)
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!");
301 for (int i = 0; i < touchedSlots; i++) {
302 if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
303 array [index++] = new KeyValuePair<TKey, TValue> (keySlots [i], valueSlots [i]);
307 private void Resize ()
309 // From the SDK docs:
310 // Hashtable is automatically increased
311 // to the smallest prime number that is larger
312 // than twice the current number of Hashtable buckets
313 int newSize = Hashtable.ToPrime ((table.Length << 1) | 1);
315 // allocate new hash table and link slots array
316 int [] newTable = new int [newSize];
317 Link [] newLinkSlots = new Link [newSize];
319 for (int i = 0; i < table.Length; i++) {
320 int cur = table [i] - 1;
321 while (cur != NO_SLOT) {
322 int hashCode = newLinkSlots [cur].HashCode = hcp.GetHashCode(keySlots [cur]) | HASH_FLAG;
323 int index = (hashCode & int.MaxValue) % newSize;
324 newLinkSlots [cur].Next = newTable [index] - 1;
325 newTable [index] = cur + 1;
326 cur = linkSlots [cur].Next;
329 table = newTable;
330 linkSlots = newLinkSlots;
332 // allocate new data slots, copy data
333 TKey [] newKeySlots = new TKey [newSize];
334 TValue [] newValueSlots = new TValue [newSize];
335 Array.Copy (keySlots, 0, newKeySlots, 0, touchedSlots);
336 Array.Copy (valueSlots, 0, newValueSlots, 0, touchedSlots);
337 keySlots = newKeySlots;
338 valueSlots = newValueSlots;
340 threshold = (int)(newSize * DEFAULT_LOAD_FACTOR);
343 public void Add (TKey key, TValue value)
345 if (key == null)
346 throw new ArgumentNullException ("key");
348 // get first item of linked list corresponding to given key
349 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
350 int index = (hashCode & int.MaxValue) % table.Length;
351 int cur = table [index] - 1;
353 // walk linked list until end is reached (throw an exception if a
354 // existing slot is found having an equivalent key)
355 while (cur != NO_SLOT) {
356 // The ordering is important for compatibility with MS and strange
357 // Object.Equals () implementations
358 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
359 throw new ArgumentException ("An element with the same key already exists in the dictionary.");
360 cur = linkSlots [cur].Next;
363 if (++count > threshold) {
364 Resize ();
365 index = (hashCode & int.MaxValue) % table.Length;
368 // find an empty slot
369 cur = emptySlot;
370 if (cur == NO_SLOT)
371 cur = touchedSlots++;
372 else
373 emptySlot = linkSlots [cur].Next;
375 // store the hash code of the added item,
376 // prepend the added item to its linked list,
377 // update the hash table
378 linkSlots [cur].HashCode = hashCode;
379 linkSlots [cur].Next = table [index] - 1;
380 table [index] = cur + 1;
382 // store item's data
383 keySlots [cur] = key;
384 valueSlots [cur] = value;
386 generation++;
389 public IEqualityComparer<TKey> Comparer {
390 get { return hcp; }
393 public void Clear ()
395 count = 0;
396 // clear the hash table
397 Array.Clear (table, 0, table.Length);
398 // clear arrays
399 Array.Clear (keySlots, 0, keySlots.Length);
400 Array.Clear (valueSlots, 0, valueSlots.Length);
401 Array.Clear (linkSlots, 0, linkSlots.Length);
403 // empty the "empty slots chain"
404 emptySlot = NO_SLOT;
406 touchedSlots = 0;
407 generation++;
410 public bool ContainsKey (TKey key)
412 if (key == null)
413 throw new ArgumentNullException ("key");
415 // get first item of linked list corresponding to given key
416 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
417 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
419 // walk linked list until right slot is found or end is reached
420 while (cur != NO_SLOT) {
421 // The ordering is important for compatibility with MS and strange
422 // Object.Equals () implementations
423 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
424 return true;
425 cur = linkSlots [cur].Next;
428 return false;
431 public bool ContainsValue (TValue value)
433 IEqualityComparer<TValue> cmp = EqualityComparer<TValue>.Default;
435 for (int i = 0; i < table.Length; i++) {
436 int cur = table [i] - 1;
437 while (cur != NO_SLOT) {
438 if (cmp.Equals (valueSlots [cur], value))
439 return true;
440 cur = linkSlots [cur].Next;
443 return false;
446 [SecurityPermission (SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
447 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
449 if (info == null)
450 throw new ArgumentNullException ("info");
452 info.AddValue ("Version", generation);
453 info.AddValue ("Comparer", hcp);
454 KeyValuePair<TKey, TValue> [] data = null;
455 if (count > 0) {
456 data = new KeyValuePair<TKey,TValue> [count];
457 CopyTo (data, 0);
459 info.AddValue ("HashSize", table.Length);
460 info.AddValue ("KeyValuePairs", data);
463 public virtual void OnDeserialization (object sender)
465 if (serialization_info == null)
466 return;
468 generation = serialization_info.GetInt32 ("Version");
469 hcp = (IEqualityComparer<TKey>) serialization_info.GetValue ("Comparer", typeof (IEqualityComparer<TKey>));
471 int hashSize = serialization_info.GetInt32 ("HashSize");
472 KeyValuePair<TKey, TValue> [] data =
473 (KeyValuePair<TKey, TValue> [])
474 serialization_info.GetValue ("KeyValuePairs", typeof (KeyValuePair<TKey, TValue> []));
476 if (hashSize < INITIAL_SIZE)
477 hashSize = INITIAL_SIZE;
478 InitArrays (hashSize);
479 count = 0;
481 if (data != null) {
482 for (int i = 0; i < data.Length; ++i)
483 Add (data [i].Key, data [i].Value);
485 generation++;
486 serialization_info = null;
489 public bool Remove (TKey key)
491 if (key == null)
492 throw new ArgumentNullException ("key");
494 // get first item of linked list corresponding to given key
495 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
496 int index = (hashCode & int.MaxValue) % table.Length;
497 int cur = table [index] - 1;
499 // if there is no linked list, return false
500 if (cur == NO_SLOT)
501 return false;
503 // walk linked list until right slot (and its predecessor) is
504 // found or end is reached
505 int prev = NO_SLOT;
506 do {
507 // The ordering is important for compatibility with MS and strange
508 // Object.Equals () implementations
509 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
510 break;
511 prev = cur;
512 cur = linkSlots [cur].Next;
513 } while (cur != NO_SLOT);
515 // if we reached the end of the chain, return false
516 if (cur == NO_SLOT)
517 return false;
519 count--;
520 // remove slot from linked list
521 // is slot at beginning of linked list?
522 if (prev == NO_SLOT)
523 table [index] = linkSlots [cur].Next + 1;
524 else
525 linkSlots [prev].Next = linkSlots [cur].Next;
527 // mark slot as empty and prepend it to "empty slots chain"
528 linkSlots [cur].Next = emptySlot;
529 emptySlot = cur;
531 linkSlots [cur].HashCode = 0;
532 // clear empty key and value slots
533 keySlots [cur] = default (TKey);
534 valueSlots [cur] = default (TValue);
536 generation++;
537 return true;
540 public bool TryGetValue (TKey key, out TValue value)
542 if (key == null)
543 throw new ArgumentNullException ("key");
545 // get first item of linked list corresponding to given key
546 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
547 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
549 // walk linked list until right slot is found or end is reached
550 while (cur != NO_SLOT) {
551 // The ordering is important for compatibility with MS and strange
552 // Object.Equals () implementations
553 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key)) {
554 value = valueSlots [cur];
555 return true;
557 cur = linkSlots [cur].Next;
560 // we did not find the slot
561 value = default (TValue);
562 return false;
565 ICollection<TKey> IDictionary<TKey, TValue>.Keys {
566 get { return Keys; }
569 ICollection<TValue> IDictionary<TKey, TValue>.Values {
570 get { return Values; }
573 public KeyCollection Keys {
574 get { return new KeyCollection (this); }
577 public ValueCollection Values {
578 get { return new ValueCollection (this); }
581 ICollection IDictionary.Keys {
582 get { return Keys; }
585 ICollection IDictionary.Values {
586 get { return Values; }
589 bool IDictionary.IsFixedSize {
590 get { return false; }
593 bool IDictionary.IsReadOnly {
594 get { return false; }
597 TKey ToTKey (object key)
599 if (key == null)
600 throw new ArgumentNullException ("key");
601 if (!(key is TKey))
602 throw new ArgumentException ("not of type: " + typeof (TKey).ToString (), "key");
603 return (TKey) key;
606 TValue ToTValue (object value)
608 if (value == null && !typeof (TValue).IsValueType)
609 return default (TValue);
610 if (!(value is TValue))
611 throw new ArgumentException ("not of type: " + typeof (TValue).ToString (), "value");
612 return (TValue) value;
615 object IDictionary.this [object key] {
616 get {
617 if (key is TKey && ContainsKey((TKey) key))
618 return this [ToTKey (key)];
619 return null;
621 set { this [ToTKey (key)] = ToTValue (value); }
624 void IDictionary.Add (object key, object value)
626 this.Add (ToTKey (key), ToTValue (value));
629 bool IDictionary.Contains (object key)
631 if (key == null)
632 throw new ArgumentNullException ("key");
633 if (key is TKey)
634 return ContainsKey ((TKey) key);
635 return false;
638 void IDictionary.Remove (object key)
640 if (key == null)
641 throw new ArgumentNullException ("key");
642 if (key is TKey)
643 Remove ((TKey) key);
646 bool ICollection.IsSynchronized {
647 get { return false; }
650 object ICollection.SyncRoot {
651 get { return this; }
654 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
655 get { return false; }
658 void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair)
660 Add (keyValuePair.Key, keyValuePair.Value);
663 bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair)
665 return ContainsKeyValuePair (keyValuePair);
668 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
670 this.CopyTo (array, index);
673 bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair)
675 if (!ContainsKeyValuePair (keyValuePair))
676 return false;
678 return Remove (keyValuePair.Key);
681 bool ContainsKeyValuePair (KeyValuePair<TKey, TValue> pair)
683 TValue value;
684 if (!TryGetValue (pair.Key, out value))
685 return false;
687 return EqualityComparer<TValue>.Default.Equals (pair.Value, value);
690 void ICollection.CopyTo (Array array, int index)
692 if (array == null)
693 throw new ArgumentNullException ("array");
694 if (index < 0)
695 throw new ArgumentOutOfRangeException ("index");
696 // we want no exception for index==array.Length && Count == 0
697 if (index > array.Length)
698 throw new ArgumentException ("index larger than largest valid index of array");
699 if (array.Length - index < count)
700 throw new ArgumentException ("Destination array cannot hold the requested elements!");
702 KeyValuePair<TKey, TValue> [] pairs = array as KeyValuePair<TKey, TValue> [];
703 if (pairs != null) {
704 this.CopyTo (pairs, index);
705 return;
708 DictionaryEntry [] entries = array as DictionaryEntry [];
709 if (entries != null) {
710 for (int i = 0; i < touchedSlots; i++) {
711 if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
712 entries [index++] = new DictionaryEntry (keySlots [i], valueSlots [i]);
714 return;
717 object [] objects = array as object [];
718 if (objects != null && objects.GetType () == typeof (object [])) {
719 for (int i = 0; i < touchedSlots; i++) {
720 if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
721 objects [index++] = new KeyValuePair<TKey, TValue> (keySlots [i], valueSlots [i]);
723 return;
726 throw new ArgumentException ("Invalid array type");
729 IEnumerator IEnumerable.GetEnumerator ()
731 return new Enumerator (this);
734 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
736 return new Enumerator (this);
739 IDictionaryEnumerator IDictionary.GetEnumerator ()
741 return new ShimEnumerator (this);
744 public Enumerator GetEnumerator ()
746 return new Enumerator (this);
749 [Serializable]
750 private class ShimEnumerator : IDictionaryEnumerator, IEnumerator
752 Enumerator host_enumerator;
753 public ShimEnumerator (Dictionary<TKey, TValue> host)
755 host_enumerator = host.GetEnumerator ();
758 public void Dispose ()
760 host_enumerator.Dispose ();
763 public bool MoveNext ()
765 return host_enumerator.MoveNext ();
768 public DictionaryEntry Entry {
769 get { return ((IDictionaryEnumerator) host_enumerator).Entry; }
772 public object Key {
773 get { return host_enumerator.Current.Key; }
776 public object Value {
777 get { return host_enumerator.Current.Value; }
780 // This is the raison d' etre of this $%!@$%@^@ class.
781 // We want: IDictionary.GetEnumerator ().Current is DictionaryEntry
782 public object Current {
783 get { return Entry; }
786 public void Reset ()
788 host_enumerator.Reset ();
792 [Serializable]
793 public struct Enumerator : IEnumerator<KeyValuePair<TKey,TValue>>,
794 IDisposable, IDictionaryEnumerator, IEnumerator
796 Dictionary<TKey, TValue> dictionary;
797 int cur;
798 int stamp;
799 const int NOT_STARTED = -1; // must be -1
801 internal Enumerator (Dictionary<TKey, TValue> dictionary)
803 this.dictionary = dictionary;
804 stamp = dictionary.generation;
806 cur = NOT_STARTED;
809 public bool MoveNext ()
811 VerifyState ();
812 while (cur < dictionary.touchedSlots) {
813 if ((dictionary.linkSlots [++cur].HashCode & HASH_FLAG) != 0)
814 return true;
816 return false;
819 public KeyValuePair<TKey, TValue> Current {
820 get {
821 VerifyCurrent ();
822 return new KeyValuePair <TKey, TValue> (
823 dictionary.keySlots [cur],
824 dictionary.valueSlots [cur]
829 internal TKey CurrentKey {
830 get {
831 VerifyCurrent ();
832 return dictionary.keySlots [cur];
836 internal TValue CurrentValue {
837 get {
838 VerifyCurrent ();
839 return dictionary.valueSlots [cur];
843 object IEnumerator.Current {
844 get { return Current; }
847 void IEnumerator.Reset ()
849 Reset ();
852 internal void Reset ()
854 cur = NOT_STARTED;
857 DictionaryEntry IDictionaryEnumerator.Entry {
858 get {
859 VerifyCurrent ();
860 return new DictionaryEntry (
861 dictionary.keySlots [cur],
862 dictionary.valueSlots [cur]
867 object IDictionaryEnumerator.Key {
868 get {
869 VerifyCurrent();
870 return dictionary.keySlots [cur];
874 object IDictionaryEnumerator.Value {
875 get {
876 VerifyCurrent();
877 return dictionary.valueSlots [cur];
881 void VerifyState ()
883 if (dictionary == null)
884 throw new ObjectDisposedException (null);
885 if (dictionary.generation != stamp)
886 throw new InvalidOperationException ("out of sync");
889 void VerifyCurrent ()
891 VerifyState ();
892 if (cur == NOT_STARTED || cur >= dictionary.touchedSlots)
893 throw new InvalidOperationException ("Current is not valid");
896 public void Dispose ()
898 dictionary = null;
902 // This collection is a read only collection
903 [Serializable]
904 public sealed class KeyCollection : ICollection<TKey>, IEnumerable<TKey>, ICollection, IEnumerable {
905 Dictionary<TKey, TValue> dictionary;
907 public KeyCollection (Dictionary<TKey, TValue> dictionary)
909 if (dictionary == null)
910 throw new ArgumentNullException ("dictionary");
911 this.dictionary = dictionary;
914 void CopyToCheck (IList array, int index)
916 if (array == null)
917 throw new ArgumentNullException ("array");
918 if (index < 0)
919 throw new ArgumentOutOfRangeException ("index");
920 // we want no exception for index==array.Length && dictionary.Count == 0
921 if (index > array.Count)
922 throw new ArgumentException ("index larger than largest valid index of array");
923 if (array.Count - index < dictionary.Count)
924 throw new ArgumentException ("Destination array cannot hold the requested elements!");
927 public void CopyTo (TKey [] array, int index)
929 CopyToCheck ((IList)array, index);
930 for (int i = 0; i < dictionary.touchedSlots; i++) {
931 if ((dictionary.linkSlots [i].HashCode & HASH_FLAG) != 0)
932 array [index++] = dictionary.keySlots [i];
936 public Enumerator GetEnumerator ()
938 return new Enumerator (dictionary);
941 void ICollection<TKey>.Add (TKey item)
943 throw new NotSupportedException ("this is a read-only collection");
946 void ICollection<TKey>.Clear ()
948 throw new NotSupportedException ("this is a read-only collection");
951 bool ICollection<TKey>.Contains (TKey item)
953 return dictionary.ContainsKey (item);
956 bool ICollection<TKey>.Remove (TKey item)
958 throw new NotSupportedException ("this is a read-only collection");
961 IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator ()
963 return this.GetEnumerator ();
966 void ICollection.CopyTo (Array array, int index)
968 IList list = array;
969 CopyToCheck (list, index);
970 for (int i = 0; i < dictionary.touchedSlots; i++) {
971 if ((dictionary.linkSlots [i].HashCode & HASH_FLAG) != 0)
972 list [index++] = dictionary.keySlots [i];
976 IEnumerator IEnumerable.GetEnumerator ()
978 return this.GetEnumerator ();
981 public int Count {
982 get { return dictionary.Count; }
985 bool ICollection<TKey>.IsReadOnly {
986 get { return true; }
989 bool ICollection.IsSynchronized {
990 get { return false; }
993 object ICollection.SyncRoot {
994 get { return ((ICollection) dictionary).SyncRoot; }
997 [Serializable]
998 public struct Enumerator : IEnumerator<TKey>, IDisposable, IEnumerator {
999 Dictionary<TKey, TValue>.Enumerator host_enumerator;
1001 internal Enumerator (Dictionary<TKey, TValue> host)
1003 host_enumerator = host.GetEnumerator ();
1006 public void Dispose ()
1008 host_enumerator.Dispose ();
1011 public bool MoveNext ()
1013 return host_enumerator.MoveNext ();
1016 public TKey Current {
1017 get { return host_enumerator.CurrentKey; }
1020 object IEnumerator.Current {
1021 get { return host_enumerator.CurrentKey; }
1024 void IEnumerator.Reset ()
1026 host_enumerator.Reset ();
1031 // This collection is a read only collection
1032 [Serializable]
1033 public sealed class ValueCollection : ICollection<TValue>, IEnumerable<TValue>, ICollection, IEnumerable {
1034 Dictionary<TKey, TValue> dictionary;
1036 public ValueCollection (Dictionary<TKey, TValue> dictionary)
1038 if (dictionary == null)
1039 throw new ArgumentNullException ("dictionary");
1040 this.dictionary = dictionary;
1043 public void CopyTo (TValue [] array, int index)
1045 if (array == null)
1046 throw new ArgumentNullException ("array");
1047 if (index < 0)
1048 throw new ArgumentOutOfRangeException ("index");
1049 // we want no exception for index==array.Length && dictionary.Count == 0
1050 if (index > array.Length)
1051 throw new ArgumentException ("index larger than largest valid index of array");
1052 if (array.Length - index < dictionary.Count)
1053 throw new ArgumentException ("Destination array cannot hold the requested elements!");
1055 for (int i = 0; i < dictionary.touchedSlots; i++) {
1056 if ((dictionary.linkSlots [i].HashCode & HASH_FLAG) != 0)
1057 array [index++] = dictionary.valueSlots [i];
1061 public Enumerator GetEnumerator ()
1063 return new Enumerator (dictionary);
1066 void ICollection<TValue>.Add (TValue item)
1068 throw new NotSupportedException ("this is a read-only collection");
1071 void ICollection<TValue>.Clear ()
1073 throw new NotSupportedException ("this is a read-only collection");
1076 bool ICollection<TValue>.Contains (TValue item)
1078 return dictionary.ContainsValue (item);
1081 bool ICollection<TValue>.Remove (TValue item)
1083 throw new NotSupportedException ("this is a read-only collection");
1086 IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator ()
1088 return this.GetEnumerator ();
1091 void ICollection.CopyTo (Array array, int index)
1093 CopyTo ((TValue []) array, index);
1096 IEnumerator IEnumerable.GetEnumerator ()
1098 return this.GetEnumerator ();
1101 public int Count {
1102 get { return dictionary.Count; }
1105 bool ICollection<TValue>.IsReadOnly {
1106 get { return true; }
1109 bool ICollection.IsSynchronized {
1110 get { return false; }
1113 object ICollection.SyncRoot {
1114 get { return ((ICollection) dictionary).SyncRoot; }
1117 [Serializable]
1118 public struct Enumerator : IEnumerator<TValue>, IDisposable, IEnumerator {
1119 Dictionary<TKey, TValue>.Enumerator host_enumerator;
1121 internal Enumerator (Dictionary<TKey,TValue> host)
1123 host_enumerator = host.GetEnumerator ();
1126 public void Dispose ()
1128 host_enumerator.Dispose();
1131 public bool MoveNext ()
1133 return host_enumerator.MoveNext ();
1136 public TValue Current {
1137 get { return host_enumerator.CurrentValue; }
1140 object IEnumerator.Current {
1141 get { return host_enumerator.CurrentValue; }
1144 void IEnumerator.Reset ()
1146 host_enumerator.Reset ();
1152 #endif