2 // System.Collections.Generic.SortedList.cs
5 // Sergey Chaban (serge@wildwestsoftware.com)
6 // Duncan Mak (duncan@ximian.com)
7 // Herve Poussineau (hpoussineau@fr.st
8 // Zoltan Varga (vargaz@gmail.com)
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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.
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.InteropServices;
38 using System.Diagnostics;
40 namespace System.Collections.Generic
43 /// Represents a collection of associated keys and values
44 /// that are sorted by the keys and are accessible by key
49 [DebuggerDisplay ("Count={Count}")]
50 [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
51 public class SortedList<TKey, TValue> : IDictionary<TKey, TValue>,
54 ICollection<KeyValuePair<TKey, TValue>>,
55 IEnumerable<KeyValuePair<TKey, TValue>>,
58 private readonly static int INITIAL_SIZE = 16;
60 private enum EnumeratorMode : int { KEY_MODE = 0, VALUE_MODE, ENTRY_MODE }
63 private int modificationCount;
64 private KeyValuePair<TKey, TValue>[] table;
65 private IComparer<TKey> comparer;
66 private int defaultCapacity;
72 : this (INITIAL_SIZE, null)
76 public SortedList (int capacity)
77 : this (capacity, null)
81 public SortedList (int capacity, IComparer<TKey> comparer)
84 throw new ArgumentOutOfRangeException ("initialCapacity");
89 defaultCapacity = INITIAL_SIZE;
90 Init (comparer, capacity, true);
93 public SortedList (IComparer<TKey> comparer) : this (INITIAL_SIZE, comparer)
97 public SortedList (IDictionary<TKey, TValue> dictionary) : this (dictionary, null)
101 public SortedList (IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
103 if (dictionary == null)
104 throw new ArgumentNullException ("dictionary");
106 Init (comparer, dictionary.Count, true);
108 foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
109 Add (kvp.Key, kvp.Value);
124 bool ICollection.IsSynchronized {
130 Object ICollection.SyncRoot {
138 bool IDictionary.IsFixedSize {
144 bool IDictionary.IsReadOnly {
150 public TValue this [TKey key] {
153 throw new ArgumentNullException("key");
158 return table [i].Value;
160 throw new KeyNotFoundException ();
164 throw new ArgumentNullException("key");
166 PutImpl (key, value, true);
170 object IDictionary.this [object key] {
175 return this [(TKey)key];
179 this [ToKey (key)] = ToValue (value);
183 public int Capacity {
189 int current = this.table.Length;
192 throw new ArgumentOutOfRangeException("capacity too small");
194 else if (value == 0) {
195 // return to default size
196 KeyValuePair<TKey, TValue> [] newTable = new KeyValuePair<TKey, TValue> [defaultCapacity];
197 Array.Copy (table, newTable, inUse);
198 this.table = newTable;
200 else if (value > inUse) {
201 KeyValuePair<TKey, TValue> [] newTable = new KeyValuePair<TKey, TValue> [value];
202 Array.Copy (table, newTable, inUse);
203 this.table = newTable;
205 else if (value > current) {
206 KeyValuePair<TKey, TValue> [] newTable = new KeyValuePair<TKey, TValue> [value];
207 Array.Copy (table, newTable, current);
208 this.table = newTable;
213 public IList<TKey> Keys {
215 return new ListKeys (this);
219 public IList<TValue> Values {
221 return new ListValues (this);
225 ICollection IDictionary.Keys {
227 return new ListKeys (this);
231 ICollection IDictionary.Values {
233 return new ListValues (this);
237 ICollection<TKey> IDictionary<TKey, TValue>.Keys {
243 ICollection<TValue> IDictionary<TKey, TValue>.Values {
249 public IComparer<TKey> Comparer {
255 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
262 // Public instance methods.
265 public void Add (TKey key, TValue value)
268 throw new ArgumentNullException ("key");
270 PutImpl (key, value, false);
273 public bool ContainsKey (TKey key)
276 throw new ArgumentNullException ("key");
278 return (Find (key) >= 0);
281 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator ()
283 for (int i = 0; i < inUse; i ++) {
284 KeyValuePair<TKey, TValue> current = this.table [i];
286 yield return new KeyValuePair<TKey, TValue> (current.Key, current.Value);
290 public bool Remove (TKey key)
293 throw new ArgumentNullException ("key");
295 int i = IndexOfKey (key);
304 // ICollection<KeyValuePair<TKey, TValue>>
306 void ICollection<KeyValuePair<TKey, TValue>>.Clear ()
313 Array.Clear (table, 0, table.Length);
318 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int arrayIndex)
324 throw new ArgumentNullException();
327 throw new ArgumentOutOfRangeException();
329 if (arrayIndex >= array.Length)
330 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
331 if (Count > (array.Length - arrayIndex))
332 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
335 foreach (KeyValuePair<TKey, TValue> pair in this)
339 void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair) {
340 Add (keyValuePair.Key, keyValuePair.Value);
343 bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair) {
344 int i = Find (keyValuePair.Key);
347 return Comparer<KeyValuePair<TKey, TValue>>.Default.Compare (table [i], keyValuePair) == 0;
352 bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair) {
353 int i = Find (keyValuePair.Key);
355 if (i >= 0 && (Comparer<KeyValuePair<TKey, TValue>>.Default.Compare (table [i], keyValuePair) == 0)) {
363 // IEnumerable<KeyValuePair<TKey, TValue>>
365 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
367 for (int i = 0; i < inUse; i ++) {
368 KeyValuePair<TKey, TValue> current = this.table [i];
370 yield return new KeyValuePair<TKey, TValue> (current.Key, current.Value);
376 IEnumerator IEnumerable.GetEnumerator ()
378 return GetEnumerator ();
383 void IDictionary.Add (object key, object value)
385 PutImpl (ToKey (key), ToValue (value), false);
388 bool IDictionary.Contains (object key)
391 throw new ArgumentNullException();
395 return (Find ((TKey)key) >= 0);
398 IDictionaryEnumerator IDictionary.GetEnumerator ()
400 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
403 void IDictionary.Remove (object key)
406 throw new ArgumentNullException ("key");
409 int i = IndexOfKey ((TKey)key);
410 if (i >= 0) RemoveAt (i);
415 void ICollection.CopyTo (Array array, int arrayIndex)
421 throw new ArgumentNullException();
424 throw new ArgumentOutOfRangeException();
427 throw new ArgumentException("array is multi-dimensional");
428 if (arrayIndex >= array.Length)
429 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
430 if (Count > (array.Length - arrayIndex))
431 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
433 IEnumerator<KeyValuePair<TKey,TValue>> it = GetEnumerator ();
436 while (it.MoveNext ()) {
437 array.SetValue (it.Current, i++);
442 // SortedList<TKey, TValue>
445 public void RemoveAt (int index)
447 KeyValuePair<TKey, TValue> [] table = this.table;
449 if (index >= 0 && index < cnt) {
450 if (index != cnt - 1) {
451 Array.Copy (table, index+1, table, index, cnt-1-index);
453 table [index] = default (KeyValuePair <TKey, TValue>);
458 throw new ArgumentOutOfRangeException("index out of range");
462 public int IndexOfKey (TKey key)
465 throw new ArgumentNullException ("key");
467 int indx = Find (key);
469 return (indx | (indx >> 31));
472 public int IndexOfValue (TValue value)
477 for (int i = 0; i < inUse; i ++) {
478 KeyValuePair<TKey, TValue> current = this.table [i];
480 if (Equals (value, current.Value))
487 public bool ContainsValue (TValue value)
489 return IndexOfValue (value) >= 0;
492 public void TrimExcess ()
494 if (inUse < table.Length * 0.9)
498 public bool TryGetValue (TKey key, out TValue value)
501 throw new ArgumentNullException("key");
506 value = table [i].Value;
510 value = default (TValue);
519 private void EnsureCapacity (int n, int free)
521 KeyValuePair<TKey, TValue> [] table = this.table;
522 KeyValuePair<TKey, TValue> [] newTable = null;
524 bool gap = (free >=0 && free < Count);
527 newTable = new KeyValuePair<TKey, TValue> [n << 1];
530 if (newTable != null) {
534 Array.Copy (table, 0, newTable, 0, copyLen);
536 copyLen = Count - free;
538 Array.Copy (table, free, newTable, free+1, copyLen);
541 // Just a resizing, copy the entire table.
542 Array.Copy (table, newTable, Count);
544 this.table = newTable;
546 Array.Copy (table, free, table, free+1, Count - free);
550 private void PutImpl (TKey key, TValue value, bool overwrite)
553 throw new ArgumentNullException ("null key");
555 KeyValuePair<TKey, TValue> [] table = this.table;
557 int freeIndx = Find (key);
561 throw new ArgumentException("element already exists");
563 table [freeIndx] = new KeyValuePair <TKey, TValue> (key, value);
568 freeIndx = ~freeIndx;
570 if (freeIndx > Capacity + 1)
571 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
574 EnsureCapacity (Count+1, freeIndx);
577 table [freeIndx] = new KeyValuePair <TKey, TValue> (key, value);
584 private void Init (IComparer<TKey> comparer, int capacity, bool forceSize)
586 if (comparer == null)
587 comparer = Comparer<TKey>.Default;
588 this.comparer = comparer;
589 if (!forceSize && (capacity < defaultCapacity))
590 capacity = defaultCapacity;
591 this.table = new KeyValuePair<TKey, TValue> [capacity];
593 this.modificationCount = 0;
596 private void CopyToArray (Array arr, int i,
600 throw new ArgumentNullException ("arr");
602 if (i < 0 || i + this.Count > arr.Length)
603 throw new ArgumentOutOfRangeException ("i");
605 IEnumerator it = new Enumerator (this, mode);
607 while (it.MoveNext ()) {
608 arr.SetValue (it.Current, i++);
612 private int Compare (TKey a, TKey b)
615 return comparer.Compare (a, b);
616 } catch (Exception ex) {
617 throw new InvalidOperationException ("Failed to compare two elements.", ex);
621 private int Find (TKey key)
623 KeyValuePair<TKey, TValue> [] table = this.table;
626 if (len == 0) return ~0;
631 while (left <= right) {
632 int guess = (left + right) >> 1;
634 int cmp = Compare (table[guess].Key, key);
635 if (cmp == 0) return guess;
637 if (cmp < 0) left = guess+1;
638 else right = guess-1;
644 private TKey ToKey (object key) {
646 throw new ArgumentNullException ("key");
648 throw new ArgumentException ("The value \"" + key + "\" isn't of type \"" + typeof (TKey) + "\" and can't be used in this generic collection.", "key");
652 private TValue ToValue (object value) {
653 if (!(value is TValue))
654 throw new ArgumentException ("The value \"" + value + "\" isn't of type \"" + typeof (TValue) + "\" and can't be used in this generic collection.", "value");
655 return (TValue)value;
658 internal TKey KeyAt (int index) {
659 if (index >= 0 && index < Count)
660 return table [index].Key;
662 throw new ArgumentOutOfRangeException("Index out of range");
665 internal TValue ValueAt (int index) {
666 if (index >= 0 && index < Count)
667 return table [index].Value;
669 throw new ArgumentOutOfRangeException("Index out of range");
677 private sealed class Enumerator : ICloneable, IDictionaryEnumerator, IEnumerator {
679 private SortedList<TKey, TValue>host;
683 private EnumeratorMode mode;
685 private object currentKey;
686 private object currentValue;
688 bool invalid = false;
690 private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";
692 public Enumerator (SortedList<TKey, TValue>host, EnumeratorMode mode)
695 stamp = host.modificationCount;
701 public Enumerator (SortedList<TKey, TValue>host)
702 : this (host, EnumeratorMode.ENTRY_MODE)
708 if (host.modificationCount != stamp || invalid)
709 throw new InvalidOperationException (xstr);
716 public bool MoveNext ()
718 if (host.modificationCount != stamp || invalid)
719 throw new InvalidOperationException (xstr);
721 KeyValuePair<TKey, TValue> [] table = host.table;
724 KeyValuePair<TKey, TValue> entry = table [pos];
726 currentKey = entry.Key;
727 currentValue = entry.Value;
736 public DictionaryEntry Entry
739 if (invalid || pos >= size || pos == -1)
740 throw new InvalidOperationException (xstr);
742 return new DictionaryEntry (currentKey,
749 if (invalid || pos >= size || pos == -1)
750 throw new InvalidOperationException (xstr);
755 public Object Value {
757 if (invalid || pos >= size || pos == -1)
758 throw new InvalidOperationException (xstr);
763 public Object Current {
765 if (invalid || pos >= size || pos == -1)
766 throw new InvalidOperationException (xstr);
769 case EnumeratorMode.KEY_MODE:
771 case EnumeratorMode.VALUE_MODE:
773 case EnumeratorMode.ENTRY_MODE:
777 throw new NotSupportedException (mode + " is not a supported mode.");
784 public object Clone ()
786 Enumerator e = new Enumerator (host, mode);
790 e.currentKey = currentKey;
791 e.currentValue = currentValue;
798 struct KeyEnumerator : IEnumerator <TKey>, IDisposable {
799 const int NOT_STARTED = -2;
801 // this MUST be -1, because we depend on it in move next.
802 // we just decr the size, so, 0 - 1 == FINISHED
803 const int FINISHED = -1;
805 SortedList <TKey, TValue> l;
809 internal KeyEnumerator (SortedList<TKey, TValue> l)
813 ver = l.modificationCount;
816 public void Dispose ()
821 public bool MoveNext ()
823 if (ver != l.modificationCount)
824 throw new InvalidOperationException ("Collection was modified after the enumerator was instantiated.");
826 if (idx == NOT_STARTED)
829 return idx != FINISHED && -- idx != FINISHED;
832 public TKey Current {
835 throw new InvalidOperationException ();
837 return l.KeyAt (l.Count - 1 - idx);
841 void IEnumerator.Reset ()
843 if (ver != l.modificationCount)
844 throw new InvalidOperationException ("Collection was modified after the enumerator was instantiated.");
849 object IEnumerator.Current {
850 get { return Current; }
855 struct ValueEnumerator : IEnumerator <TValue>, IDisposable {
856 const int NOT_STARTED = -2;
858 // this MUST be -1, because we depend on it in move next.
859 // we just decr the size, so, 0 - 1 == FINISHED
860 const int FINISHED = -1;
862 SortedList <TKey, TValue> l;
866 internal ValueEnumerator (SortedList<TKey, TValue> l)
870 ver = l.modificationCount;
873 public void Dispose ()
878 public bool MoveNext ()
880 if (ver != l.modificationCount)
881 throw new InvalidOperationException ("Collection was modified after the enumerator was instantiated.");
883 if (idx == NOT_STARTED)
886 return idx != FINISHED && -- idx != FINISHED;
889 public TValue Current {
892 throw new InvalidOperationException ();
894 return l.ValueAt (l.Count - 1 - idx);
898 void IEnumerator.Reset ()
900 if (ver != l.modificationCount)
901 throw new InvalidOperationException ("Collection was modified after the enumerator was instantiated.");
906 object IEnumerator.Current {
907 get { return Current; }
911 private class ListKeys : IList<TKey>, ICollection, IEnumerable {
913 private SortedList<TKey, TValue> host;
915 public ListKeys (SortedList<TKey, TValue> host)
918 throw new ArgumentNullException ();
925 public virtual void Add (TKey item) {
926 throw new NotSupportedException();
929 public virtual bool Remove (TKey key) {
930 throw new NotSupportedException ();
933 public virtual void Clear () {
934 throw new NotSupportedException();
937 public virtual void CopyTo (TKey[] array, int arrayIndex) {
941 throw new ArgumentNullException ("array");
943 throw new ArgumentOutOfRangeException();
944 if (arrayIndex >= array.Length)
945 throw new ArgumentOutOfRangeException ("arrayIndex is greater than or equal to array.Length");
946 if (Count > (array.Length - arrayIndex))
947 throw new ArgumentOutOfRangeException("Not enough space in array from arrayIndex to end of array");
950 for (int i = 0; i < Count; ++i)
951 array [j ++] = host.KeyAt (i);
954 public virtual bool Contains (TKey item) {
955 return host.IndexOfKey (item) > -1;
961 public virtual int IndexOf (TKey item) {
962 return host.IndexOfKey (item);
965 public virtual void Insert (int index, TKey item) {
966 throw new NotSupportedException ();
969 public virtual void RemoveAt (int index) {
970 throw new NotSupportedException ();
973 public virtual TKey this [int index] {
975 return host.KeyAt (index);
978 throw new NotSupportedException("attempt to modify a key");
986 public virtual IEnumerator<TKey> GetEnumerator ()
988 /* We couldn't use yield as it does not support Reset () */
989 return new KeyEnumerator (host);
996 public virtual int Count {
1002 public virtual bool IsSynchronized {
1004 return ((ICollection)host).IsSynchronized;
1008 public virtual bool IsReadOnly {
1014 public virtual Object SyncRoot {
1016 return ((ICollection)host).SyncRoot;
1020 public virtual void CopyTo (Array array, int arrayIndex)
1022 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
1029 IEnumerator IEnumerable.GetEnumerator ()
1031 for (int i = 0; i < host.Count; ++i)
1032 yield return host.KeyAt (i);
1036 private class ListValues : IList<TValue>, ICollection, IEnumerable {
1038 private SortedList<TKey, TValue>host;
1040 public ListValues (SortedList<TKey, TValue>host)
1043 throw new ArgumentNullException ();
1048 // ICollection<TValue>
1050 public virtual void Add (TValue item) {
1051 throw new NotSupportedException();
1054 public virtual bool Remove (TValue value) {
1055 throw new NotSupportedException ();
1058 public virtual void Clear () {
1059 throw new NotSupportedException();
1062 public virtual void CopyTo (TValue[] array, int arrayIndex) {
1063 if (host.Count == 0)
1066 throw new ArgumentNullException ("array");
1068 throw new ArgumentOutOfRangeException();
1069 if (arrayIndex >= array.Length)
1070 throw new ArgumentOutOfRangeException ("arrayIndex is greater than or equal to array.Length");
1071 if (Count > (array.Length - arrayIndex))
1072 throw new ArgumentOutOfRangeException("Not enough space in array from arrayIndex to end of array");
1075 for (int i = 0; i < Count; ++i)
1076 array [j ++] = host.ValueAt (i);
1079 public virtual bool Contains (TValue item) {
1080 return host.IndexOfValue (item) > -1;
1086 public virtual int IndexOf (TValue item) {
1087 return host.IndexOfValue (item);
1090 public virtual void Insert (int index, TValue item) {
1091 throw new NotSupportedException ();
1094 public virtual void RemoveAt (int index) {
1095 throw new NotSupportedException ();
1098 public virtual TValue this [int index] {
1100 return host.ValueAt (index);
1103 throw new NotSupportedException("attempt to modify a key");
1108 // IEnumerable<TValue>
1111 public virtual IEnumerator<TValue> GetEnumerator ()
1113 /* We couldn't use yield as it does not support Reset () */
1114 return new ValueEnumerator (host);
1121 public virtual int Count {
1127 public virtual bool IsSynchronized {
1129 return ((ICollection)host).IsSynchronized;
1133 public virtual bool IsReadOnly {
1139 public virtual Object SyncRoot {
1141 return ((ICollection)host).SyncRoot;
1145 public virtual void CopyTo (Array array, int arrayIndex)
1147 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
1154 IEnumerator IEnumerable.GetEnumerator ()
1156 for (int i = 0; i < host.Count; ++i)
1157 yield return host.ValueAt (i);
1163 } // System.Collections.Generic