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 CopyTo (KeyValuePair
<TKey
, TValue
> [] 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!");
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
;
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)
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
) {
365 index
= (hashCode
& int.MaxValue
) % table
.Length
;
368 // find an empty slot
371 cur
= touchedSlots
++;
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;
383 keySlots
[cur
] = key
;
384 valueSlots
[cur
] = value;
389 public IEqualityComparer
<TKey
> Comparer
{
396 // clear the hash table
397 Array
.Clear (table
, 0, table
.Length
);
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"
410 public bool ContainsKey (TKey key
)
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
))
425 cur
= linkSlots
[cur
].Next
;
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))
440 cur
= linkSlots
[cur
].Next
;
446 [SecurityPermission (SecurityAction
.LinkDemand
, Flags
=SecurityPermissionFlag
.SerializationFormatter
)]
447 public virtual void GetObjectData (SerializationInfo info
, StreamingContext context
)
450 throw new ArgumentNullException ("info");
452 info
.AddValue ("Version", generation
);
453 info
.AddValue ("Comparer", hcp
);
454 KeyValuePair
<TKey
, TValue
> [] data
= null;
456 data
= new KeyValuePair
<TKey
,TValue
> [count
];
459 info
.AddValue ("HashSize", table
.Length
);
460 info
.AddValue ("KeyValuePairs", data
);
463 public virtual void OnDeserialization (object sender
)
465 if (serialization_info
== null)
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
);
482 for (int i
= 0; i
< data
.Length
; ++i
)
483 Add (data
[i
].Key
, data
[i
].Value
);
486 serialization_info
= null;
489 public bool Remove (TKey key
)
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
503 // walk linked list until right slot (and its predecessor) is
504 // found or end is reached
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
))
512 cur
= linkSlots
[cur
].Next
;
513 } while (cur
!= NO_SLOT
);
515 // if we reached the end of the chain, return false
520 // remove slot from linked list
521 // is slot at beginning of linked list?
523 table
[index
] = linkSlots
[cur
].Next
+ 1;
525 linkSlots
[prev
].Next
= linkSlots
[cur
].Next
;
527 // mark slot as empty and prepend it to "empty slots chain"
528 linkSlots
[cur
].Next
= emptySlot
;
531 linkSlots
[cur
].HashCode
= 0;
532 // clear empty key and value slots
533 keySlots
[cur
] = default (TKey
);
534 valueSlots
[cur
] = default (TValue
);
540 public bool TryGetValue (TKey key
, out TValue
value)
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
];
557 cur
= linkSlots
[cur
].Next
;
560 // we did not find the slot
561 value = default (TValue
);
565 ICollection
<TKey
> IDictionary
<TKey
, TValue
>.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
{
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
)
600 throw new ArgumentNullException ("key");
602 throw new ArgumentException ("not of type: " + typeof (TKey
).ToString (), "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
] {
617 if (key
is TKey
&& ContainsKey((TKey
) key
))
618 return this [ToTKey (key
)];
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
)
632 throw new ArgumentNullException ("key");
634 return ContainsKey ((TKey
) key
);
638 void IDictionary
.Remove (object key
)
641 throw new ArgumentNullException ("key");
646 bool ICollection
.IsSynchronized
{
647 get { return false; }
650 object ICollection
.SyncRoot
{
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
))
678 return Remove (keyValuePair
.Key
);
681 bool ContainsKeyValuePair (KeyValuePair
<TKey
, TValue
> pair
)
684 if (!TryGetValue (pair
.Key
, out value))
687 return EqualityComparer
<TValue
>.Default
.Equals (pair
.Value
, value);
690 void ICollection
.CopyTo (Array array
, int index
)
693 throw new ArgumentNullException ("array");
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
> [];
704 this.CopyTo (pairs
, index
);
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
]);
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
]);
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);
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; }
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; }
788 host_enumerator
.Reset ();
793 public struct Enumerator
: IEnumerator
<KeyValuePair
<TKey
,TValue
>>,
794 IDisposable
, IDictionaryEnumerator
, IEnumerator
796 Dictionary
<TKey
, TValue
> dictionary
;
799 const int NOT_STARTED
= -1; // must be -1
801 internal Enumerator (Dictionary
<TKey
, TValue
> dictionary
)
803 this.dictionary
= dictionary
;
804 stamp
= dictionary
.generation
;
809 public bool MoveNext ()
812 while (cur
< dictionary
.touchedSlots
) {
813 if ((dictionary
.linkSlots
[++cur
].HashCode
& HASH_FLAG
) != 0)
819 public KeyValuePair
<TKey
, TValue
> Current
{
822 return new KeyValuePair
<TKey
, TValue
> (
823 dictionary
.keySlots
[cur
],
824 dictionary
.valueSlots
[cur
]
829 internal TKey CurrentKey
{
832 return dictionary
.keySlots
[cur
];
836 internal TValue CurrentValue
{
839 return dictionary
.valueSlots
[cur
];
843 object IEnumerator
.Current
{
844 get { return Current; }
847 void IEnumerator
.Reset ()
852 internal void Reset ()
857 DictionaryEntry IDictionaryEnumerator
.Entry
{
860 return new DictionaryEntry (
861 dictionary
.keySlots
[cur
],
862 dictionary
.valueSlots
[cur
]
867 object IDictionaryEnumerator
.Key
{
870 return dictionary
.keySlots
[cur
];
874 object IDictionaryEnumerator
.Value
{
877 return dictionary
.valueSlots
[cur
];
883 if (dictionary
== null)
884 throw new ObjectDisposedException (null);
885 if (dictionary
.generation
!= stamp
)
886 throw new InvalidOperationException ("out of sync");
889 void VerifyCurrent ()
892 if (cur
== NOT_STARTED
|| cur
>= dictionary
.touchedSlots
)
893 throw new InvalidOperationException ("Current is not valid");
896 public void Dispose ()
902 // This collection is a read only collection
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
)
917 throw new ArgumentNullException ("array");
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
)
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 ();
982 get { return dictionary.Count; }
985 bool ICollection
<TKey
>.IsReadOnly
{
989 bool ICollection
.IsSynchronized
{
990 get { return false; }
993 object ICollection
.SyncRoot
{
994 get { return ((ICollection) dictionary).SyncRoot; }
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
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
)
1046 throw new ArgumentNullException ("array");
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 ();
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; }
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 ();