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 ()
308 defaultCapacity = INITIAL_SIZE;
309 this.table = new KeyValuePair<TKey, TValue> [defaultCapacity];
316 defaultCapacity = INITIAL_SIZE;
317 this.table = new KeyValuePair<TKey, TValue> [defaultCapacity];
322 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int arrayIndex)
328 throw new ArgumentNullException();
331 throw new ArgumentOutOfRangeException();
333 if (arrayIndex >= array.Length)
334 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
335 if (Count > (array.Length - arrayIndex))
336 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
339 foreach (KeyValuePair<TKey, TValue> pair in this)
343 void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair) {
344 Add (keyValuePair.Key, keyValuePair.Value);
347 bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair) {
348 int i = Find (keyValuePair.Key);
351 return Comparer<KeyValuePair<TKey, TValue>>.Default.Compare (table [i], keyValuePair) == 0;
356 bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair) {
357 int i = Find (keyValuePair.Key);
359 if (i >= 0 && (Comparer<KeyValuePair<TKey, TValue>>.Default.Compare (table [i], keyValuePair) == 0)) {
367 // IEnumerable<KeyValuePair<TKey, TValue>>
369 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
371 for (int i = 0; i < inUse; i ++) {
372 KeyValuePair<TKey, TValue> current = this.table [i];
374 yield return new KeyValuePair<TKey, TValue> (current.Key, current.Value);
380 IEnumerator IEnumerable.GetEnumerator ()
382 return GetEnumerator ();
387 void IDictionary.Add (object key, object value)
389 PutImpl (ToKey (key), ToValue (value), false);
392 bool IDictionary.Contains (object key)
395 throw new ArgumentNullException();
399 return (Find ((TKey)key) >= 0);
402 IDictionaryEnumerator IDictionary.GetEnumerator ()
404 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
407 void IDictionary.Remove (object key)
410 throw new ArgumentNullException ("key");
413 int i = IndexOfKey ((TKey)key);
414 if (i >= 0) RemoveAt (i);
419 void ICollection.CopyTo (Array array, int arrayIndex)
425 throw new ArgumentNullException();
428 throw new ArgumentOutOfRangeException();
431 throw new ArgumentException("array is multi-dimensional");
432 if (arrayIndex >= array.Length)
433 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
434 if (Count > (array.Length - arrayIndex))
435 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
437 IEnumerator<KeyValuePair<TKey,TValue>> it = GetEnumerator ();
440 while (it.MoveNext ()) {
441 array.SetValue (it.Current, i++);
446 // SortedList<TKey, TValue>
449 public void RemoveAt (int index)
451 KeyValuePair<TKey, TValue> [] table = this.table;
453 if (index >= 0 && index < cnt) {
454 if (index != cnt - 1) {
455 Array.Copy (table, index+1, table, index, cnt-1-index);
457 table [index] = default (KeyValuePair <TKey, TValue>);
462 throw new ArgumentOutOfRangeException("index out of range");
466 public int IndexOfKey (TKey key)
469 throw new ArgumentNullException ("key");
471 int indx = Find (key);
473 return (indx | (indx >> 31));
476 public int IndexOfValue (TValue value)
481 for (int i = 0; i < inUse; i ++) {
482 KeyValuePair<TKey, TValue> current = this.table [i];
484 if (Equals (value, current.Value))
491 public bool ContainsValue (TValue value)
493 return IndexOfValue (value) >= 0;
496 public void TrimExcess ()
498 if (inUse < table.Length * 0.9)
502 public bool TryGetValue (TKey key, out TValue value)
505 throw new ArgumentNullException("key");
510 value = table [i].Value;
514 value = default (TValue);
523 private void EnsureCapacity (int n, int free)
525 KeyValuePair<TKey, TValue> [] table = this.table;
526 KeyValuePair<TKey, TValue> [] newTable = null;
528 bool gap = (free >=0 && free < Count);
531 newTable = new KeyValuePair<TKey, TValue> [n << 1];
534 if (newTable != null) {
538 Array.Copy (table, 0, newTable, 0, copyLen);
540 copyLen = Count - free;
542 Array.Copy (table, free, newTable, free+1, copyLen);
545 // Just a resizing, copy the entire table.
546 Array.Copy (table, newTable, Count);
548 this.table = newTable;
550 Array.Copy (table, free, table, free+1, Count - free);
554 private void PutImpl (TKey key, TValue value, bool overwrite)
557 throw new ArgumentNullException ("null key");
559 KeyValuePair<TKey, TValue> [] table = this.table;
561 int freeIndx = Find (key);
565 throw new ArgumentException("element already exists");
567 table [freeIndx] = new KeyValuePair <TKey, TValue> (key, value);
572 freeIndx = ~freeIndx;
574 if (freeIndx > Capacity + 1)
575 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
578 EnsureCapacity (Count+1, freeIndx);
581 table [freeIndx] = new KeyValuePair <TKey, TValue> (key, value);
588 private void Init (IComparer<TKey> comparer, int capacity, bool forceSize)
590 if (comparer == null)
591 comparer = Comparer<TKey>.Default;
592 this.comparer = comparer;
593 if (!forceSize && (capacity < defaultCapacity))
594 capacity = defaultCapacity;
595 this.table = new KeyValuePair<TKey, TValue> [capacity];
597 this.modificationCount = 0;
600 private void CopyToArray (Array arr, int i,
604 throw new ArgumentNullException ("arr");
606 if (i < 0 || i + this.Count > arr.Length)
607 throw new ArgumentOutOfRangeException ("i");
609 IEnumerator it = new Enumerator (this, mode);
611 while (it.MoveNext ()) {
612 arr.SetValue (it.Current, i++);
616 private int Compare (TKey a, TKey b)
619 return comparer.Compare (a, b);
620 } catch (Exception ex) {
621 throw new InvalidOperationException ("Failed to compare two elements.", ex);
625 private int Find (TKey key)
627 KeyValuePair<TKey, TValue> [] table = this.table;
630 if (len == 0) return ~0;
635 while (left <= right) {
636 int guess = (left + right) >> 1;
638 int cmp = Compare (table[guess].Key, key);
639 if (cmp == 0) return guess;
641 if (cmp < 0) left = guess+1;
642 else right = guess-1;
648 private TKey ToKey (object key) {
650 throw new ArgumentNullException ("key");
652 throw new ArgumentException ("The value \"" + key + "\" isn't of type \"" + typeof (TKey) + "\" and can't be used in this generic collection.", "key");
656 private TValue ToValue (object value) {
657 if (!(value is TValue))
658 throw new ArgumentException ("The value \"" + value + "\" isn't of type \"" + typeof (TValue) + "\" and can't be used in this generic collection.", "value");
659 return (TValue)value;
662 internal TKey KeyAt (int index) {
663 if (index >= 0 && index < Count)
664 return table [index].Key;
666 throw new ArgumentOutOfRangeException("Index out of range");
669 internal TValue ValueAt (int index) {
670 if (index >= 0 && index < Count)
671 return table [index].Value;
673 throw new ArgumentOutOfRangeException("Index out of range");
681 private sealed class Enumerator : ICloneable, IDictionaryEnumerator, IEnumerator {
683 private SortedList<TKey, TValue>host;
687 private EnumeratorMode mode;
689 private object currentKey;
690 private object currentValue;
692 bool invalid = false;
694 private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";
696 public Enumerator (SortedList<TKey, TValue>host, EnumeratorMode mode)
699 stamp = host.modificationCount;
705 public Enumerator (SortedList<TKey, TValue>host)
706 : this (host, EnumeratorMode.ENTRY_MODE)
712 if (host.modificationCount != stamp || invalid)
713 throw new InvalidOperationException (xstr);
720 public bool MoveNext ()
722 if (host.modificationCount != stamp || invalid)
723 throw new InvalidOperationException (xstr);
725 KeyValuePair<TKey, TValue> [] table = host.table;
728 KeyValuePair<TKey, TValue> entry = table [pos];
730 currentKey = entry.Key;
731 currentValue = entry.Value;
740 public DictionaryEntry Entry
743 if (invalid || pos >= size || pos == -1)
744 throw new InvalidOperationException (xstr);
746 return new DictionaryEntry (currentKey,
753 if (invalid || pos >= size || pos == -1)
754 throw new InvalidOperationException (xstr);
759 public Object Value {
761 if (invalid || pos >= size || pos == -1)
762 throw new InvalidOperationException (xstr);
767 public Object Current {
769 if (invalid || pos >= size || pos == -1)
770 throw new InvalidOperationException (xstr);
773 case EnumeratorMode.KEY_MODE:
775 case EnumeratorMode.VALUE_MODE:
777 case EnumeratorMode.ENTRY_MODE:
781 throw new NotSupportedException (mode + " is not a supported mode.");
788 public object Clone ()
790 Enumerator e = new Enumerator (host, mode);
794 e.currentKey = currentKey;
795 e.currentValue = currentValue;
802 struct KeyEnumerator : IEnumerator <TKey>, IDisposable {
803 const int NOT_STARTED = -2;
805 // this MUST be -1, because we depend on it in move next.
806 // we just decr the size, so, 0 - 1 == FINISHED
807 const int FINISHED = -1;
809 SortedList <TKey, TValue> l;
813 internal KeyEnumerator (SortedList<TKey, TValue> l)
817 ver = l.modificationCount;
820 public void Dispose ()
825 public bool MoveNext ()
827 if (ver != l.modificationCount)
828 throw new InvalidOperationException ("Collection was modified after the enumerator was instantiated.");
830 if (idx == NOT_STARTED)
833 return idx != FINISHED && -- idx != FINISHED;
836 public TKey Current {
839 throw new InvalidOperationException ();
841 return l.KeyAt (l.Count - 1 - idx);
845 void IEnumerator.Reset ()
847 if (ver != l.modificationCount)
848 throw new InvalidOperationException ("Collection was modified after the enumerator was instantiated.");
853 object IEnumerator.Current {
854 get { return Current; }
859 struct ValueEnumerator : IEnumerator <TValue>, IDisposable {
860 const int NOT_STARTED = -2;
862 // this MUST be -1, because we depend on it in move next.
863 // we just decr the size, so, 0 - 1 == FINISHED
864 const int FINISHED = -1;
866 SortedList <TKey, TValue> l;
870 internal ValueEnumerator (SortedList<TKey, TValue> l)
874 ver = l.modificationCount;
877 public void Dispose ()
882 public bool MoveNext ()
884 if (ver != l.modificationCount)
885 throw new InvalidOperationException ("Collection was modified after the enumerator was instantiated.");
887 if (idx == NOT_STARTED)
890 return idx != FINISHED && -- idx != FINISHED;
893 public TValue Current {
896 throw new InvalidOperationException ();
898 return l.ValueAt (l.Count - 1 - idx);
902 void IEnumerator.Reset ()
904 if (ver != l.modificationCount)
905 throw new InvalidOperationException ("Collection was modified after the enumerator was instantiated.");
910 object IEnumerator.Current {
911 get { return Current; }
915 private class ListKeys : IList<TKey>, ICollection, IEnumerable {
917 private SortedList<TKey, TValue> host;
919 public ListKeys (SortedList<TKey, TValue> host)
922 throw new ArgumentNullException ();
929 public virtual void Add (TKey item) {
930 throw new NotSupportedException();
933 public virtual bool Remove (TKey key) {
934 throw new NotSupportedException ();
937 public virtual void Clear () {
938 throw new NotSupportedException();
941 public virtual void CopyTo (TKey[] array, int arrayIndex) {
945 throw new ArgumentNullException ("array");
947 throw new ArgumentOutOfRangeException();
948 if (arrayIndex >= array.Length)
949 throw new ArgumentOutOfRangeException ("arrayIndex is greater than or equal to array.Length");
950 if (Count > (array.Length - arrayIndex))
951 throw new ArgumentOutOfRangeException("Not enough space in array from arrayIndex to end of array");
954 for (int i = 0; i < Count; ++i)
955 array [j ++] = host.KeyAt (i);
958 public virtual bool Contains (TKey item) {
959 return host.IndexOfKey (item) > -1;
965 public virtual int IndexOf (TKey item) {
966 return host.IndexOfKey (item);
969 public virtual void Insert (int index, TKey item) {
970 throw new NotSupportedException ();
973 public virtual void RemoveAt (int index) {
974 throw new NotSupportedException ();
977 public virtual TKey this [int index] {
979 return host.KeyAt (index);
982 throw new NotSupportedException("attempt to modify a key");
990 public virtual IEnumerator<TKey> GetEnumerator ()
992 /* We couldn't use yield as it does not support Reset () */
993 return new KeyEnumerator (host);
1000 public virtual int Count {
1006 public virtual bool IsSynchronized {
1008 return ((ICollection)host).IsSynchronized;
1012 public virtual bool IsReadOnly {
1018 public virtual Object SyncRoot {
1020 return ((ICollection)host).SyncRoot;
1024 public virtual void CopyTo (Array array, int arrayIndex)
1026 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
1033 IEnumerator IEnumerable.GetEnumerator ()
1035 for (int i = 0; i < host.Count; ++i)
1036 yield return host.KeyAt (i);
1040 private class ListValues : IList<TValue>, ICollection, IEnumerable {
1042 private SortedList<TKey, TValue>host;
1044 public ListValues (SortedList<TKey, TValue>host)
1047 throw new ArgumentNullException ();
1052 // ICollection<TValue>
1054 public virtual void Add (TValue item) {
1055 throw new NotSupportedException();
1058 public virtual bool Remove (TValue value) {
1059 throw new NotSupportedException ();
1062 public virtual void Clear () {
1063 throw new NotSupportedException();
1066 public virtual void CopyTo (TValue[] array, int arrayIndex) {
1067 if (host.Count == 0)
1070 throw new ArgumentNullException ("array");
1072 throw new ArgumentOutOfRangeException();
1073 if (arrayIndex >= array.Length)
1074 throw new ArgumentOutOfRangeException ("arrayIndex is greater than or equal to array.Length");
1075 if (Count > (array.Length - arrayIndex))
1076 throw new ArgumentOutOfRangeException("Not enough space in array from arrayIndex to end of array");
1079 for (int i = 0; i < Count; ++i)
1080 array [j ++] = host.ValueAt (i);
1083 public virtual bool Contains (TValue item) {
1084 return host.IndexOfValue (item) > -1;
1090 public virtual int IndexOf (TValue item) {
1091 return host.IndexOfValue (item);
1094 public virtual void Insert (int index, TValue item) {
1095 throw new NotSupportedException ();
1098 public virtual void RemoveAt (int index) {
1099 throw new NotSupportedException ();
1102 public virtual TValue this [int index] {
1104 return host.ValueAt (index);
1107 throw new NotSupportedException("attempt to modify a key");
1112 // IEnumerable<TValue>
1115 public virtual IEnumerator<TValue> GetEnumerator ()
1117 /* We couldn't use yield as it does not support Reset () */
1118 return new ValueEnumerator (host);
1125 public virtual int Count {
1131 public virtual bool IsSynchronized {
1133 return ((ICollection)host).IsSynchronized;
1137 public virtual bool IsReadOnly {
1143 public virtual Object SyncRoot {
1145 return ((ICollection)host).SyncRoot;
1149 public virtual void CopyTo (Array array, int arrayIndex)
1151 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
1158 IEnumerator IEnumerable.GetEnumerator ()
1160 for (int i = 0; i < host.Count; ++i)
1161 yield return host.ValueAt (i);
1167 } // System.Collections.Generic