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)
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Copyright (C) 2005 David Waite
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 using System.Collections;
38 using System.Collections.Generic;
39 using System.Runtime.Serialization;
40 using System.Security.Permissions;
41 using System.Runtime.InteropServices;
43 namespace System.Collections.Generic {
47 public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>,
50 ICollection<KeyValuePair<TKey, TValue>>,
51 IEnumerable<KeyValuePair<TKey, TValue>>,
53 IDeserializationCallback
55 const int INITIAL_SIZE = 10;
56 const float DEFAULT_LOAD_FACTOR = (90f / 100);
59 // Can't use KeyValuePair here since the JIT won't inline the Key/Value accessors
64 public Slot (TKey Key, TValue Value, Slot Next)
74 private int threshold;
76 IEqualityComparer<TKey> hcp;
77 SerializationInfo serialization_info;
79 private uint generation;
82 get { return used_slots; }
83 /* FIXME: this should be 'private' not 'internal'. */
90 // This is perf critical so inline calls etc.
91 public TValue this [TKey key] {
94 throw new ArgumentNullException ("key");
95 uint size = (uint)table.Length;
96 uint i = ((uint)hcp.GetHashCode (key) & Int32.MaxValue) % size;
97 Slot slot = table [i];
98 while (slot != null) {
99 // The ordering is important for compatibility with MS and strange
100 // Object.Equals () implementations
101 if (hcp.Equals (slot.Key, key))
105 throw new KeyNotFoundException ();
110 Slot prev = GetPrev (key, out index);
111 Slot slot = prev == null ? table [index] : prev.Next;
113 if (Count++ >= threshold) {
115 index = DoHash (key);
117 table [index] = new Slot (key, value, table [index]);
121 // move-to-front on update
122 prev.Next = slot.Next;
123 slot.Next = table [index];
124 table [index] = slot;
134 Init (INITIAL_SIZE, null);
137 public Dictionary (IEqualityComparer<TKey> comparer)
139 Init (INITIAL_SIZE, comparer);
142 public Dictionary (IDictionary<TKey, TValue> dictionary)
143 : this (dictionary, null)
147 public Dictionary (int capacity)
149 Init (capacity, null);
152 public Dictionary (IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
154 if (dictionary == null)
155 throw new ArgumentNullException ("dictionary");
156 int capacity = dictionary.Count;
157 Init (capacity, comparer);
158 foreach (KeyValuePair<TKey, TValue> entry in dictionary)
159 this.Add (entry.Key, entry.Value);
162 public Dictionary (int capacity, IEqualityComparer<TKey> comparer)
164 Init (capacity, comparer);
167 protected Dictionary (SerializationInfo info, StreamingContext context)
169 serialization_info = info;
174 threshold = (int)(table.Length * DEFAULT_LOAD_FACTOR);
175 if (threshold == 0 && table.Length > 0)
179 private void Init (int capacity, IEqualityComparer<TKey> hcp)
182 throw new ArgumentOutOfRangeException ("capacity");
183 this.hcp = (hcp != null) ? hcp : EqualityComparer<TKey>.Default;
185 capacity = INITIAL_SIZE;
187 /* Modify capacity so 'capacity' elements can be added without resizing */
188 capacity = (int)(capacity / DEFAULT_LOAD_FACTOR) + 1;
190 table = new Slot [capacity];
195 void CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
198 throw new ArgumentNullException ("array");
200 throw new ArgumentOutOfRangeException ("index");
201 // we want no exception for index==array.Length && Count == 0
202 if (index > array.Length)
203 throw new ArgumentException ("index larger than largest valid index of array");
204 if (array.Length - index < Count)
205 throw new ArgumentException ("Destination array cannot hold the requested elements!");
207 for (int i = 0; i < table.Length; ++i) {
208 for (Slot slot = table [i]; slot != null; slot = slot.Next)
209 array [index++] = new KeyValuePair<TKey, TValue> (slot.Key, slot.Value);
213 private void Resize ()
215 // From the SDK docs:
216 // Hashtable is automatically increased
217 // to the smallest prime number that is larger
218 // than twice the current number of Hashtable buckets
219 uint newSize = (uint) Hashtable.ToPrime ((table.Length << 1) | 1);
221 Slot nextslot = null;
222 Slot [] oldTable = table;
224 table = new Slot [newSize];
228 for (int i = 0; i < oldTable.Length; i++) {
229 for (Slot slot = oldTable [i]; slot != null; slot = nextslot) {
230 nextslot = slot.Next;
232 index = DoHash (slot.Key);
233 slot.Next = table [index];
234 table [index] = slot;
239 public void Add (TKey key, TValue value)
242 Slot slot = GetSlot (key, out index);
244 throw new ArgumentException ("An element with the same key already exists in the dictionary.");
245 DoAdd (index, key, value);
248 void DoAdd (int index, TKey key, TValue value)
250 if (Count++ >= threshold) {
252 index = DoHash (key);
254 table [index] = new Slot (key, value, table [index]);
257 private int DoHash (TKey key)
259 uint size = (uint)this.table.Length;
260 uint h = (uint)hcp.GetHashCode (key) & Int32.MaxValue;
261 int spot = (int) (h % size);
265 public IEqualityComparer<TKey> Comparer {
272 for (int i = 0; i < table.Length; i++)
276 public bool ContainsKey (TKey key)
279 return GetSlot (key, out index) != null;
282 public bool ContainsValue (TValue value)
284 IEqualityComparer<TValue> cmp = EqualityComparer<TValue>.Default;
286 for (int i = 0; i < table.Length; ++i) {
287 for (Slot slot = table [i]; slot != null; slot = slot.Next) {
288 if (cmp.Equals (slot.Value, value))
295 [SecurityPermission (SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
296 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
299 throw new ArgumentNullException ("info");
301 info.AddValue ("hcp", hcp);
302 KeyValuePair<TKey, TValue> [] data = null;
304 data = new KeyValuePair<TKey,TValue> [Count];
307 info.AddValue ("data", data);
308 info.AddValue ("buckets_hint", table.Length);
311 public virtual void OnDeserialization (object sender)
313 if (serialization_info == null)
316 hcp = (IEqualityComparer<TKey>) serialization_info.GetValue ("hcp", typeof (IEqualityComparer<TKey>));
317 KeyValuePair<TKey, TValue> [] data =
318 (KeyValuePair<TKey, TValue> [])
319 serialization_info.GetValue ("data", typeof (KeyValuePair<TKey, TValue> []));
321 int buckets = serialization_info.GetInt32 ("buckets_hint");
322 if (buckets < INITIAL_SIZE)
323 buckets = INITIAL_SIZE;
325 table = new Slot [buckets];
330 for (int i = 0; i < data.Length; ++i)
331 Add (data [i].Key, data [i].Value);
333 serialization_info = null;
336 public bool Remove (TKey key)
339 Slot prev = GetPrev (key, out index);
340 Slot slot = prev == null ? table [index] : prev.Next;
345 table [index] = slot.Next;
347 prev.Next = slot.Next;
352 // Return the predecessor of the slot containing 'key', and set 'index' to the
353 // chain the key was found in.
354 // If the key is not found, return null and set 'index' to the chain that would've
355 // contained the key.
357 private Slot GetPrev (TKey key, out int index)
359 // This method is perf critical so inline DoHash () etc.
361 throw new ArgumentNullException ("key");
362 uint size = (uint)table.Length;
363 // Use uint to help ABCREM
364 uint i = ((uint)hcp.GetHashCode (key) & Int32.MaxValue) % size;
365 Slot slot = table [i];
369 // The ordering is important for compatibility with MS and strange
370 // Object.Equals () implementations
371 if (hcp.Equals (slot.Key, key))
375 } while (slot != null);
385 private Slot GetSlot (TKey key, out int index)
387 Slot prev = GetPrev (key, out index);
388 return prev == null ? table [index] : prev.Next;
391 public bool TryGetValue (TKey key, out TValue value)
394 Slot slot = GetSlot (key, out index);
395 bool found = slot != null;
396 value = found ? slot.Value : default (TValue);
400 ICollection<TKey> IDictionary<TKey, TValue>.Keys {
404 ICollection<TValue> IDictionary<TKey, TValue>.Values {
405 get { return Values; }
408 public KeyCollection Keys {
409 get { return new KeyCollection (this); }
412 public ValueCollection Values {
413 get { return new ValueCollection (this); }
416 ICollection IDictionary.Keys {
420 ICollection IDictionary.Values {
421 get { return Values; }
424 bool IDictionary.IsFixedSize {
425 get { return false; }
428 bool IDictionary.IsReadOnly {
429 get { return false; }
432 TKey ToTKey (object key)
435 throw new ArgumentNullException ("key");
437 throw new ArgumentException ("not of type: " + typeof (TKey).ToString (), "key");
441 TValue ToTValue (object value)
443 if (!(value is TValue))
444 throw new ArgumentException ("not of type: " + typeof (TValue).ToString (), "value");
445 return (TValue) value;
448 object IDictionary.this [object key] {
449 get { return this [ToTKey (key)]; }
450 set { this [ToTKey (key)] = ToTValue (value); }
453 void IDictionary.Add (object key, object value)
455 this.Add (ToTKey (key), ToTValue (value));
458 bool IDictionary.Contains (object key)
461 throw new ArgumentNullException ("key");
463 return ContainsKey ((TKey) key);
467 void IDictionary.Remove (object key)
470 throw new ArgumentNullException ("key");
475 bool ICollection.IsSynchronized {
476 get { return false; }
479 object ICollection.SyncRoot {
483 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
484 get { return false; }
487 void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair)
489 Add (keyValuePair.Key, keyValuePair.Value);
492 bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair)
494 return this.ContainsKey (keyValuePair.Key);
497 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
499 this.CopyTo (array, index);
502 bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair)
504 return Remove (keyValuePair.Key);
507 void ICollection.CopyTo (Array array, int index)
510 throw new ArgumentNullException ("array");
512 throw new ArgumentOutOfRangeException ("index");
513 // we want no exception for index==array.Length && Count == 0
514 if (index > array.Length)
515 throw new ArgumentException ("index larger than largest valid index of array");
516 if (array.Length - index < Count)
517 throw new ArgumentException ("Destination array cannot hold the requested elements!");
519 for (int i = 0; i < table.Length; ++i) {
520 for (Slot slot = table [i]; slot != null; slot = slot.Next)
521 array.SetValue (new DictionaryEntry (slot.Key, slot.Value), index++);
525 IEnumerator IEnumerable.GetEnumerator ()
527 return new Enumerator (this);
530 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
532 return new Enumerator (this);
535 IDictionaryEnumerator IDictionary.GetEnumerator ()
537 return new ShimEnumerator (this);
540 public Enumerator GetEnumerator ()
542 return new Enumerator (this);
546 private class ShimEnumerator : IDictionaryEnumerator, IEnumerator
548 Enumerator host_enumerator;
549 public ShimEnumerator (Dictionary<TKey, TValue> host)
551 host_enumerator = host.GetEnumerator ();
554 public void Dispose ()
556 host_enumerator.Dispose ();
559 public bool MoveNext ()
561 return host_enumerator.MoveNext ();
564 public DictionaryEntry Entry {
565 get { return ((IDictionaryEnumerator) host_enumerator).Entry; }
569 get { return host_enumerator.Current.Key; }
572 public object Value {
573 get { return host_enumerator.Current.Value; }
576 // This is the raison d' etre of this $%!@$%@^@ class.
577 // We want: IDictionary.GetEnumerator ().Current is DictionaryEntry
578 public object Current {
579 get { return Entry; }
584 ((IEnumerator)host_enumerator).Reset ();
589 public struct Enumerator : IEnumerator<KeyValuePair<TKey,TValue>>,
590 IDisposable, IDictionaryEnumerator, IEnumerator
592 Dictionary<TKey, TValue> dictionary;
598 internal Enumerator (Dictionary<TKey, TValue> dictionary)
600 this.dictionary = dictionary;
601 stamp = dictionary.generation;
603 // The following stanza is identical to IEnumerator.Reset (),
604 // but because of the definite assignment rule, we cannot call it here.
609 public bool MoveNext ()
613 // Pre-condition: current == null => this is the first call to MoveNext ()
615 current = current.Next;
617 while (current == null && next_index < dictionary.table.Length)
618 current = dictionary.table [next_index++];
620 // Post-condition: current == null => this is the last call to MoveNext()
621 return current != null;
624 public KeyValuePair<TKey, TValue> Current {
626 Slot s = CurrentSlot ();
627 return new KeyValuePair <TKey, TValue> (s.Key, s.Value);
631 object IEnumerator.Current {
632 get { return Current; }
635 void IEnumerator.Reset ()
641 DictionaryEntry IDictionaryEnumerator.Entry {
643 Slot s = CurrentSlot ();
644 return new DictionaryEntry (s.Key, s.Value);
648 object IDictionaryEnumerator.Key {
649 get { return Current.Key; }
652 object IDictionaryEnumerator.Value {
653 get { return Current.Value; }
658 if (dictionary == null)
659 throw new ObjectDisposedException (null);
660 if (dictionary.generation != stamp)
661 throw new InvalidOperationException ("out of sync");
668 throw new InvalidOperationException ("Current is not valid");
672 public void Dispose ()
679 // This collection is a read only collection
681 public sealed class KeyCollection : ICollection<TKey>, IEnumerable<TKey>, ICollection, IEnumerable {
682 Dictionary<TKey, TValue> dictionary;
684 public KeyCollection (Dictionary<TKey, TValue> dictionary)
686 if (dictionary == null)
687 throw new ArgumentNullException ("dictionary");
688 this.dictionary = dictionary;
691 public void CopyTo (TKey [] array, int index)
694 throw new ArgumentNullException ("array");
696 throw new ArgumentOutOfRangeException ("index");
697 // we want no exception for index==array.Length && dictionary.Count == 0
698 if (index > array.Length)
699 throw new ArgumentException ("index larger than largest valid index of array");
700 if (array.Length - index < dictionary.Count)
701 throw new ArgumentException ("Destination array cannot hold the requested elements!");
703 foreach (TKey k in this)
707 public Enumerator GetEnumerator ()
709 return new Enumerator (dictionary);
712 void ICollection<TKey>.Add (TKey item)
714 throw new NotSupportedException ("this is a read-only collection");
717 void ICollection<TKey>.Clear ()
719 throw new NotSupportedException ("this is a read-only collection");
722 bool ICollection<TKey>.Contains (TKey item)
724 return dictionary.ContainsKey (item);
727 bool ICollection<TKey>.Remove (TKey item)
729 throw new NotSupportedException ("this is a read-only collection");
732 IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator ()
734 return this.GetEnumerator ();
737 void ICollection.CopyTo (Array array, int index)
739 CopyTo ((TKey []) array, index);
742 IEnumerator IEnumerable.GetEnumerator ()
744 return this.GetEnumerator ();
748 get { return dictionary.Count; }
751 bool ICollection<TKey>.IsReadOnly {
755 bool ICollection.IsSynchronized {
756 get { return false; }
759 object ICollection.SyncRoot {
760 get { return ((ICollection) dictionary).SyncRoot; }
764 public struct Enumerator : IEnumerator<TKey>, IDisposable, IEnumerator {
765 Dictionary<TKey, TValue>.Enumerator host_enumerator;
767 internal Enumerator (Dictionary<TKey, TValue> host)
769 host_enumerator = host.GetEnumerator ();
772 public void Dispose ()
774 host_enumerator.Dispose ();
777 public bool MoveNext ()
779 return host_enumerator.MoveNext ();
782 public TKey Current {
783 get { return host_enumerator.Current.Key; }
786 object IEnumerator.Current {
787 get { return host_enumerator.Current.Key; }
790 void IEnumerator.Reset ()
792 ((IEnumerator)host_enumerator).Reset ();
797 // This collection is a read only collection
799 public sealed class ValueCollection : ICollection<TValue>, IEnumerable<TValue>, ICollection, IEnumerable {
800 Dictionary<TKey, TValue> dictionary;
802 public ValueCollection (Dictionary<TKey, TValue> dictionary)
804 if (dictionary == null)
805 throw new ArgumentNullException ("dictionary");
806 this.dictionary = dictionary;
809 public void CopyTo (TValue [] array, int index)
812 throw new ArgumentNullException ("array");
814 throw new ArgumentOutOfRangeException ("index");
815 // we want no exception for index==array.Length && dictionary.Count == 0
816 if (index > array.Length)
817 throw new ArgumentException ("index larger than largest valid index of array");
818 if (array.Length - index < dictionary.Count)
819 throw new ArgumentException ("Destination array cannot hold the requested elements!");
821 foreach (TValue k in this)
825 public Enumerator GetEnumerator ()
827 return new Enumerator (dictionary);
830 void ICollection<TValue>.Add (TValue item)
832 throw new NotSupportedException ("this is a read-only collection");
835 void ICollection<TValue>.Clear ()
837 throw new NotSupportedException ("this is a read-only collection");
840 bool ICollection<TValue>.Contains (TValue item)
842 return dictionary.ContainsValue (item);
845 bool ICollection<TValue>.Remove (TValue item)
847 throw new NotSupportedException ("this is a read-only collection");
850 IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator ()
852 return this.GetEnumerator ();
855 void ICollection.CopyTo (Array array, int index)
857 CopyTo ((TValue []) array, index);
860 IEnumerator IEnumerable.GetEnumerator ()
862 return this.GetEnumerator ();
866 get { return dictionary.Count; }
869 bool ICollection<TValue>.IsReadOnly {
873 bool ICollection.IsSynchronized {
874 get { return false; }
877 object ICollection.SyncRoot {
878 get { return ((ICollection) dictionary).SyncRoot; }
882 public struct Enumerator : IEnumerator<TValue>, IDisposable, IEnumerator {
883 Dictionary<TKey, TValue>.Enumerator host_enumerator;
885 internal Enumerator (Dictionary<TKey,TValue> host)
887 host_enumerator = host.GetEnumerator ();
890 public void Dispose ()
892 host_enumerator.Dispose();
895 public bool MoveNext ()
897 return host_enumerator.MoveNext ();
900 public TValue Current {
901 get { return host_enumerator.Current.Value; }
904 object IEnumerator.Current {
905 get { return host_enumerator.Current.Value; }
908 void IEnumerator.Reset ()
910 ((IEnumerator)host_enumerator).Reset ();