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.
37 using System.Collections;
38 using System.Globalization;
39 using System.Runtime.InteropServices;
41 namespace System.Collections.Generic {
44 /// Represents a collection of associated keys and values
45 /// that are sorted by the keys and are accessible by key
50 public class SortedList<TKey, TValue> : IDictionary<TKey, TValue>,
53 ICollection<KeyValuePair<TKey, TValue>>,
54 IEnumerable<KeyValuePair<TKey, TValue>>,
57 private readonly static int INITIAL_SIZE = 16;
59 private enum EnumeratorMode : int { KEY_MODE = 0, VALUE_MODE, ENTRY_MODE }
62 private int modificationCount;
63 private KeyValuePair<TKey, TValue>[] table;
64 private IComparer<TKey> comparer;
65 private int defaultCapacity;
71 : this (INITIAL_SIZE, null)
75 public SortedList (int capacity)
76 : this (capacity, null)
80 public SortedList (int capacity, IComparer<TKey> comparer)
83 throw new ArgumentOutOfRangeException ("initialCapacity");
88 defaultCapacity = INITIAL_SIZE;
89 Init (comparer, capacity, true);
92 public SortedList (IComparer<TKey> comparer) : this (INITIAL_SIZE, comparer)
96 public SortedList (IDictionary<TKey, TValue> dictionary) : this (dictionary, null)
100 public SortedList (IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
102 if (dictionary == null)
103 throw new ArgumentNullException ("dictionary");
105 Init (comparer, dictionary.Count, true);
107 foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
108 Add (kvp.Key, kvp.Value);
117 public virtual int Count {
123 bool ICollection.IsSynchronized {
129 Object ICollection.SyncRoot {
137 bool IDictionary.IsFixedSize {
143 bool IDictionary.IsReadOnly {
149 public virtual TValue this [TKey key] {
152 throw new ArgumentNullException("key");
157 return table [i].Value;
159 throw new KeyNotFoundException ();
163 throw new ArgumentNullException("key");
165 PutImpl (key, value, true);
169 object IDictionary.this [object key] {
174 return this [(TKey)key];
178 this [ToKey (key)] = ToValue (value);
182 public int Capacity {
188 int current = this.table.Length;
191 throw new ArgumentOutOfRangeException("capacity too small");
193 else if (value == 0) {
194 // return to default size
195 KeyValuePair<TKey, TValue> [] newTable = new KeyValuePair<TKey, TValue> [defaultCapacity];
196 Array.Copy (table, newTable, inUse);
197 this.table = newTable;
200 else if (current > defaultCapacity && value < current) {
201 KeyValuePair<TKey, TValue> [] newTable = new KeyValuePair<TKey, TValue> [defaultCapacity];
202 Array.Copy (table, newTable, inUse);
203 this.table = newTable;
206 else if (value > inUse) {
207 KeyValuePair<TKey, TValue> [] newTable = new KeyValuePair<TKey, TValue> [value];
208 Array.Copy (table, newTable, inUse);
209 this.table = newTable;
211 else if (value > current) {
212 KeyValuePair<TKey, TValue> [] newTable = new KeyValuePair<TKey, TValue> [value];
213 Array.Copy (table, newTable, current);
214 this.table = newTable;
219 public IList<TKey> Keys {
221 return new ListKeys (this);
225 public IList<TValue> Values {
227 return new ListValues (this);
231 ICollection IDictionary.Keys {
233 return new ListKeys (this);
237 ICollection IDictionary.Values {
239 return new ListValues (this);
243 ICollection<TKey> IDictionary<TKey, TValue>.Keys {
249 ICollection<TValue> IDictionary<TKey, TValue>.Values {
255 public IComparer<TKey> Comparer {
261 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
268 // Public instance methods.
271 // IDictionary<TKey, TValue>
273 void IDictionary<TKey,TValue>.Add (TKey key, TValue value)
276 throw new ArgumentNullException ("key");
278 PutImpl (key, value, false);
281 public virtual void Add (TKey key, TValue value)
284 throw new ArgumentNullException ("key");
286 PutImpl (key, value, false);
289 public bool ContainsKey (TKey key)
292 throw new ArgumentNullException ("key");
294 return (Find (key) >= 0);
297 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator ()
299 for (int i = 0; i < inUse; i ++) {
300 KeyValuePair<TKey, TValue> current = this.table [i];
302 yield return new KeyValuePair<TKey, TValue> (current.Key, current.Value);
306 bool IDictionary<TKey,TValue>.Remove (TKey key)
309 throw new ArgumentNullException ("key");
311 int i = IndexOfKey (key);
320 public virtual bool Remove (TKey key)
323 throw new ArgumentNullException ("key");
325 int i = IndexOfKey (key);
334 // ICollection<KeyValuePair<TKey, TValue>>
336 void ICollection<KeyValuePair<TKey, TValue>>.Clear ()
338 defaultCapacity = INITIAL_SIZE;
339 this.table = new KeyValuePair<TKey, TValue> [defaultCapacity];
344 public virtual void Clear ()
346 defaultCapacity = INITIAL_SIZE;
347 this.table = new KeyValuePair<TKey, TValue> [defaultCapacity];
352 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int arrayIndex)
355 throw new ArgumentNullException();
358 throw new ArgumentOutOfRangeException();
360 if (arrayIndex >= array.Length)
361 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
362 if (Count > (array.Length - arrayIndex))
363 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
366 foreach (KeyValuePair<TKey, TValue> pair in this)
370 void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair) {
371 Add (keyValuePair.Key, keyValuePair.Value);
374 bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair) {
375 int i = Find (keyValuePair.Key);
378 return Comparer<KeyValuePair<TKey, TValue>>.Default.Compare (table [i], keyValuePair) == 0;
383 bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair) {
384 int i = Find (keyValuePair.Key);
386 if (i >= 0 && (Comparer<KeyValuePair<TKey, TValue>>.Default.Compare (table [i], keyValuePair) == 0)) {
394 // IEnumerable<KeyValuePair<TKey, TValue>>
396 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
398 for (int i = 0; i < inUse; i ++) {
399 KeyValuePair<TKey, TValue> current = this.table [i];
401 yield return new KeyValuePair<TKey, TValue> (current.Key, current.Value);
407 IEnumerator IEnumerable.GetEnumerator ()
409 return GetEnumerator ();
414 void IDictionary.Add (object key, object value)
416 PutImpl (ToKey (key), ToValue (value), false);
419 bool IDictionary.Contains (object key)
422 throw new ArgumentNullException();
426 return (Find ((TKey)key) >= 0);
429 IDictionaryEnumerator IDictionary.GetEnumerator ()
431 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
434 void IDictionary.Remove (object key)
437 throw new ArgumentNullException ("key");
440 int i = IndexOfKey ((TKey)key);
441 if (i >= 0) RemoveAt (i);
446 void ICollection.CopyTo (Array array, int arrayIndex)
449 throw new ArgumentNullException();
452 throw new ArgumentOutOfRangeException();
455 throw new ArgumentException("array is multi-dimensional");
456 if (arrayIndex >= array.Length)
457 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
458 if (Count > (array.Length - arrayIndex))
459 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
461 IEnumerator<KeyValuePair<TKey,TValue>> it = GetEnumerator ();
464 while (it.MoveNext ()) {
465 array.SetValue (it.Current, i++);
470 // SortedList<TKey, TValue>
473 public void RemoveAt (int index)
475 KeyValuePair<TKey, TValue> [] table = this.table;
477 if (index >= 0 && index < cnt) {
478 if (index != cnt - 1) {
479 Array.Copy (table, index+1, table, index, cnt-1-index);
481 table [index] = default (KeyValuePair <TKey, TValue>);
486 throw new ArgumentOutOfRangeException("index out of range");
490 public int IndexOfKey (TKey key)
493 throw new ArgumentNullException ("key");
498 } catch (Exception) {
499 throw new InvalidOperationException();
502 return (indx | (indx >> 31));
505 public int IndexOfValue (TValue value)
510 for (int i = 0; i < inUse; i ++) {
511 KeyValuePair<TKey, TValue> current = this.table [i];
513 if (Equals (value, current.Value))
520 public bool ContainsValue (TValue value)
522 return IndexOfValue (value) >= 0;
525 public void TrimExcess ()
527 if (inUse < table.Length * 0.9)
531 public bool TryGetValue (TKey key, out TValue value)
534 throw new ArgumentNullException("key");
539 value = table [i].Value;
543 value = default (TValue);
552 private void EnsureCapacity (int n, int free)
554 KeyValuePair<TKey, TValue> [] table = this.table;
555 KeyValuePair<TKey, TValue> [] newTable = null;
557 bool gap = (free >=0 && free < Count);
560 newTable = new KeyValuePair<TKey, TValue> [n << 1];
563 if (newTable != null) {
567 Array.Copy (table, 0, newTable, 0, copyLen);
569 copyLen = Count - free;
571 Array.Copy (table, free, newTable, free+1, copyLen);
574 // Just a resizing, copy the entire table.
575 Array.Copy (table, newTable, Count);
577 this.table = newTable;
579 Array.Copy (table, free, table, free+1, Count - free);
583 private void PutImpl (TKey key, TValue value, bool overwrite)
586 throw new ArgumentNullException ("null key");
588 KeyValuePair<TKey, TValue> [] table = this.table;
593 freeIndx = Find (key);
594 } catch (Exception) {
595 throw new InvalidOperationException();
600 throw new ArgumentException("element already exists");
602 table [freeIndx] = new KeyValuePair <TKey, TValue> (key, value);
607 freeIndx = ~freeIndx;
609 if (freeIndx > Capacity + 1)
610 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
613 EnsureCapacity (Count+1, freeIndx);
616 table [freeIndx] = new KeyValuePair <TKey, TValue> (key, value);
623 private void Init (IComparer<TKey> comparer, int capacity, bool forceSize)
625 if (comparer == null)
626 comparer = Comparer<TKey>.Default;
627 this.comparer = comparer;
628 if (!forceSize && (capacity < defaultCapacity))
629 capacity = defaultCapacity;
630 this.table = new KeyValuePair<TKey, TValue> [capacity];
632 this.modificationCount = 0;
635 private void CopyToArray (Array arr, int i,
639 throw new ArgumentNullException ("arr");
641 if (i < 0 || i + this.Count > arr.Length)
642 throw new ArgumentOutOfRangeException ("i");
644 IEnumerator it = new Enumerator (this, mode);
646 while (it.MoveNext ()) {
647 arr.SetValue (it.Current, i++);
651 private int Find (TKey key)
653 KeyValuePair<TKey, TValue> [] table = this.table;
656 if (len == 0) return ~0;
661 while (left <= right) {
662 int guess = (left + right) >> 1;
664 int cmp = comparer.Compare (key, table[guess].Key);
665 if (cmp == 0) return guess;
667 if (cmp > 0) left = guess+1;
668 else right = guess-1;
674 private TKey ToKey (object key) {
676 throw new ArgumentNullException ("key");
678 throw new ArgumentException ("The value \"" + key + "\" isn't of type \"" + typeof (TKey) + "\" and can't be used in this generic collection.", "key");
682 private TValue ToValue (object value) {
683 if (!(value is TValue))
684 throw new ArgumentException ("The value \"" + value + "\" isn't of type \"" + typeof (TValue) + "\" and can't be used in this generic collection.", "value");
685 return (TValue)value;
688 internal TKey KeyAt (int index) {
689 if (index >= 0 && index < Count)
690 return table [index].Key;
692 throw new ArgumentOutOfRangeException("Index out of range");
695 internal TValue ValueAt (int index) {
696 if (index >= 0 && index < Count)
697 return table [index].Value;
699 throw new ArgumentOutOfRangeException("Index out of range");
707 private sealed class Enumerator : ICloneable, IDictionaryEnumerator, IEnumerator {
709 private SortedList<TKey, TValue>host;
713 private EnumeratorMode mode;
715 private object currentKey;
716 private object currentValue;
718 bool invalid = false;
720 private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";
722 public Enumerator (SortedList<TKey, TValue>host, EnumeratorMode mode)
725 stamp = host.modificationCount;
731 public Enumerator (SortedList<TKey, TValue>host)
732 : this (host, EnumeratorMode.ENTRY_MODE)
738 if (host.modificationCount != stamp || invalid)
739 throw new InvalidOperationException (xstr);
746 public bool MoveNext ()
748 if (host.modificationCount != stamp || invalid)
749 throw new InvalidOperationException (xstr);
751 KeyValuePair<TKey, TValue> [] table = host.table;
754 KeyValuePair<TKey, TValue> entry = table [pos];
756 currentKey = entry.Key;
757 currentValue = entry.Value;
766 public DictionaryEntry Entry
769 if (invalid || pos >= size || pos == -1)
770 throw new InvalidOperationException (xstr);
772 return new DictionaryEntry (currentKey,
779 if (invalid || pos >= size || pos == -1)
780 throw new InvalidOperationException (xstr);
785 public Object Value {
787 if (invalid || pos >= size || pos == -1)
788 throw new InvalidOperationException (xstr);
793 public Object Current {
795 if (invalid || pos >= size || pos == -1)
796 throw new InvalidOperationException (xstr);
799 case EnumeratorMode.KEY_MODE:
801 case EnumeratorMode.VALUE_MODE:
803 case EnumeratorMode.ENTRY_MODE:
807 throw new NotSupportedException (mode + " is not a supported mode.");
814 public object Clone ()
816 Enumerator e = new Enumerator (host, mode);
820 e.currentKey = currentKey;
821 e.currentValue = currentValue;
828 private class ListKeys : IList<TKey>, ICollection, IEnumerable {
830 private SortedList<TKey, TValue> host;
832 public ListKeys (SortedList<TKey, TValue> host)
835 throw new ArgumentNullException ();
842 public virtual void Add (TKey item) {
843 throw new NotSupportedException();
846 public virtual bool Remove (TKey key) {
847 throw new NotSupportedException ();
850 public virtual void Clear () {
851 throw new NotSupportedException();
854 public virtual void CopyTo (TKey[] array, int arrayIndex) {
856 throw new ArgumentNullException ("array");
858 throw new ArgumentOutOfRangeException();
859 if (arrayIndex >= array.Length)
860 throw new ArgumentOutOfRangeException ("arrayIndex is greater than or equal to array.Length");
861 if (Count > (array.Length - arrayIndex))
862 throw new ArgumentOutOfRangeException("Not enough space in array from arrayIndex to end of array");
865 for (int i = 0; i < Count; ++i)
866 array [j ++] = host.KeyAt (i);
869 public virtual bool Contains (TKey item) {
870 return host.IndexOfKey (item) > -1;
876 public virtual int IndexOf (TKey item) {
877 return host.IndexOfKey (item);
880 public virtual void Insert (int index, TKey item) {
881 throw new NotSupportedException ();
884 public virtual void RemoveAt (int index) {
885 throw new NotSupportedException ();
888 public virtual TKey this [int index] {
890 return host.KeyAt (index);
893 throw new NotSupportedException("attempt to modify a key");
901 public virtual IEnumerator<TKey> GetEnumerator ()
903 for (int i = 0; i < host.Count; ++i)
904 yield return host.KeyAt (i);
911 public virtual int Count {
917 public virtual bool IsSynchronized {
919 return ((ICollection)host).IsSynchronized;
923 public virtual bool IsReadOnly {
929 public virtual Object SyncRoot {
931 return ((ICollection)host).SyncRoot;
935 public virtual void CopyTo (Array array, int arrayIndex)
937 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
944 IEnumerator IEnumerable.GetEnumerator ()
946 for (int i = 0; i < host.Count; ++i)
947 yield return host.KeyAt (i);
951 private class ListValues : IList<TValue>, ICollection, IEnumerable {
953 private SortedList<TKey, TValue>host;
955 public ListValues (SortedList<TKey, TValue>host)
958 throw new ArgumentNullException ();
963 // ICollection<TValue>
965 public virtual void Add (TValue item) {
966 throw new NotSupportedException();
969 public virtual bool Remove (TValue value) {
970 throw new NotSupportedException ();
973 public virtual void Clear () {
974 throw new NotSupportedException();
977 public virtual void CopyTo (TValue[] array, int arrayIndex) {
979 throw new ArgumentNullException ("array");
981 throw new ArgumentOutOfRangeException();
982 if (arrayIndex >= array.Length)
983 throw new ArgumentOutOfRangeException ("arrayIndex is greater than or equal to array.Length");
984 if (Count > (array.Length - arrayIndex))
985 throw new ArgumentOutOfRangeException("Not enough space in array from arrayIndex to end of array");
988 for (int i = 0; i < Count; ++i)
989 array [j ++] = host.ValueAt (i);
992 public virtual bool Contains (TValue item) {
993 return host.IndexOfValue (item) > -1;
999 public virtual int IndexOf (TValue item) {
1000 return host.IndexOfValue (item);
1003 public virtual void Insert (int index, TValue item) {
1004 throw new NotSupportedException ();
1007 public virtual void RemoveAt (int index) {
1008 throw new NotSupportedException ();
1011 public virtual TValue this [int index] {
1013 return host.ValueAt (index);
1016 throw new NotSupportedException("attempt to modify a key");
1021 // IEnumerable<TValue>
1024 public virtual IEnumerator<TValue> GetEnumerator ()
1026 for (int i = 0; i < host.Count; ++i)
1027 yield return host.ValueAt (i);
1034 public virtual int Count {
1040 public virtual bool IsSynchronized {
1042 return ((ICollection)host).IsSynchronized;
1046 public virtual bool IsReadOnly {
1052 public virtual Object SyncRoot {
1054 return ((ICollection)host).SyncRoot;
1058 public virtual void CopyTo (Array array, int arrayIndex)
1060 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
1067 IEnumerator IEnumerable.GetEnumerator ()
1069 for (int i = 0; i < host.Count; ++i)
1070 yield return host.ValueAt (i);
1076 } // System.Collections.Generic