2 // System.Collections.SortedList.cs
\r
5 // Sergey Chaban (serge@wildwestsoftware.com)
\r
6 // Duncan Mak (duncan@ximian.com)
\r
7 // Herve Poussineau (hpoussineau@fr.st
\r
12 using System.Collections;
\r
13 using System.Globalization;
\r
15 namespace System.Collections {
\r
18 /// Represents a collection of associated keys and values
\r
19 /// that are sorted by the keys and are accessible by key
\r
23 public class SortedList : IDictionary, ICollection,
\r
24 IEnumerable, ICloneable {
\r
28 internal struct Slot {
\r
29 internal Object key;
\r
30 internal Object value;
\r
33 private readonly static int INITIAL_SIZE = 16;
\r
35 public enum EnumeratorMode : int { KEY_MODE = 0, VALUE_MODE, ENTRY_MODE }
\r
38 private int modificationCount;
\r
39 private Slot[] table;
\r
40 private IComparer comparer;
\r
45 public SortedList () : this (INITIAL_SIZE)
\r
49 public SortedList (int initialCapacity)
\r
50 : this (null, initialCapacity)
\r
54 public SortedList (IComparer comparer, int initialCapacity)
\r
56 if (initialCapacity < 0)
\r
57 throw new ArgumentOutOfRangeException();
\r
59 this.comparer = comparer;
\r
60 InitTable (initialCapacity, true);
\r
63 public SortedList (IComparer comparer)
\r
65 this.comparer = comparer;
\r
66 InitTable (INITIAL_SIZE, true);
\r
70 public SortedList (IDictionary d) : this (d, null)
\r
74 public SortedList (IDictionary d, IComparer comparer)
\r
77 throw new ArgumentNullException ("dictionary");
\r
79 InitTable (d.Count, true);
\r
80 this.comparer = comparer;
\r
82 IDictionaryEnumerator it = d.GetEnumerator ();
\r
83 while (it.MoveNext ()) {
\r
84 if (it.Key is IComparable) {
\r
85 Add (it.Key, it.Value);
\r
87 throw new InvalidCastException("!IComparable");
\r
98 public virtual int Count {
\r
104 public virtual bool IsSynchronized {
\r
110 public virtual Object SyncRoot {
\r
119 public virtual bool IsFixedSize {
\r
126 public virtual bool IsReadOnly {
\r
132 public virtual ICollection Keys {
\r
134 return new ListKeys (this);
\r
138 public virtual ICollection Values {
\r
140 return new ListValues (this);
\r
146 public virtual Object this [Object key] {
\r
149 throw new ArgumentNullException();
\r
150 return GetImpl (key);
\r
154 throw new ArgumentNullException();
\r
156 throw new NotSupportedException("SortedList is Read Only.");
\r
157 if (Find(key) < 0 && IsFixedSize)
\r
158 throw new NotSupportedException("Key not found and SortedList is fixed size.");
\r
160 PutImpl (key, value, true);
\r
164 public virtual int Capacity {
\r
166 return table.Length;
\r
170 int current = this.table.Length;
\r
173 throw new ArgumentOutOfRangeException("capacity too small");
\r
175 } else if (current > INITIAL_SIZE && value < current) {
176 Slot [] newTable = new Slot [INITIAL_SIZE];
\r
177 Array.Copy (table, newTable, inUse);
\r
178 this.table = newTable;
\r
180 } else if (value > inUse) {
\r
181 Slot [] newTable = new Slot [value];
\r
182 Array.Copy (table, newTable, inUse);
\r
183 this.table = newTable;
\r
186 } else if (value > current) {
\r
187 Slot [] newTable = new Slot [value];
\r
188 Array.Copy (table, newTable, current);
\r
189 this.table = newTable;
\r
195 // Public instance methods.
\r
200 IEnumerator IEnumerable.GetEnumerator ()
\r
202 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
\r
208 public virtual void Add (object key, object value)
\r
210 PutImpl (key, value, false);
\r
214 public virtual void Clear ()
\r
216 this.table = new Slot [INITIAL_SIZE];
\r
218 modificationCount++;
\r
221 public virtual bool Contains (object key)
\r
224 throw new ArgumentNullException();
\r
227 return (Find (key) >= 0);
\r
228 } catch (Exception) {
\r
229 throw new InvalidOperationException();
\r
234 public virtual IDictionaryEnumerator GetEnumerator ()
\r
236 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
\r
239 public virtual void Remove (object key)
\r
241 int i = IndexOfKey (key);
\r
242 if (i >= 0) RemoveAt (i);
\r
248 public virtual void CopyTo (Array array, int arrayIndex)
\r
251 throw new ArgumentNullException();
\r
253 if (arrayIndex < 0)
\r
254 throw new ArgumentOutOfRangeException();
\r
256 if (array.Rank > 1)
\r
257 throw new ArgumentException("array is multi-dimensional");
\r
258 if (arrayIndex >= array.Length)
\r
259 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
\r
260 if (Count > (array.Length - arrayIndex))
\r
261 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
\r
263 IDictionaryEnumerator it = GetEnumerator ();
\r
264 int i = arrayIndex;
\r
266 while (it.MoveNext ()) {
\r
267 array.SetValue (it.Entry, i++);
\r
275 public virtual object Clone ()
\r
277 SortedList sl = new SortedList (this, comparer);
\r
278 sl.modificationCount = this.modificationCount;
\r
289 public virtual IList GetKeyList ()
\r
291 return new ListKeys (this);
\r
295 public virtual IList GetValueList ()
\r
297 return new ListValues (this);
\r
301 public virtual void RemoveAt (int index)
\r
303 Slot [] table = this.table;
\r
305 if (index >= 0 && index < cnt) {
\r
306 if (index != cnt - 1) {
\r
307 Array.Copy (table, index+1, table, index, cnt-1-index);
\r
309 table [index].key = null;
\r
310 table [index].value = null;
\r
313 ++modificationCount;
\r
315 throw new ArgumentOutOfRangeException("index out of range");
\r
319 public virtual int IndexOfKey (object key)
\r
322 throw new ArgumentNullException();
\r
327 } catch (Exception) {
\r
328 throw new InvalidOperationException();
\r
331 return (indx | (indx >> 31));
\r
335 public virtual int IndexOfValue (object value)
\r
340 for (int i = 0; i < inUse; i ++) {
\r
341 Slot current = this.table [i];
\r
343 if (Equals (current.value, value))
\r
351 public virtual bool ContainsKey (object key)
\r
354 throw new ArgumentNullException();
\r
357 return Contains (key);
\r
358 } catch (Exception) {
\r
359 throw new InvalidOperationException();
\r
364 public virtual bool ContainsValue (object value)
\r
366 return IndexOfValue (value) >= 0;
\r
370 public virtual object GetByIndex (int index)
\r
372 if (index >= 0 && index < Count)
\r
373 return table [index].value;
\r
376 throw new ArgumentOutOfRangeException("index out of range");
\r
380 public virtual void SetByIndex (int index, object value)
\r
382 if (index >= 0 && index < Count)
\r
383 table [index].value = value;
\r
386 throw new ArgumentOutOfRangeException("index out of range");
\r
390 public virtual object GetKey (int index)
\r
392 if (index >= 0 && index < Count)
\r
393 return table [index].key;
\r
396 throw new ArgumentOutOfRangeException("index out of range");
\r
399 public static SortedList Synchronized (SortedList list)
\r
402 throw new ArgumentNullException (Locale.GetText ("Base list is null."));
\r
404 return new SynchedSortedList (list);
\r
407 public virtual void TrimToSize ()
\r
410 // Trimming an empty SortedList sets the capacity
\r
411 // of the SortedList to the default capacity,
\r
414 Resize (INITIAL_SIZE, false);
\r
416 Resize (Count, true);
\r
425 private void Resize (int n, bool copy)
\r
427 Slot [] table = this.table;
\r
428 Slot [] newTable = new Slot [n];
\r
429 if (copy) Array.Copy (table, 0, newTable, 0, n);
\r
430 this.table = newTable;
\r
434 private void EnsureCapacity (int n, int free)
\r
436 Slot [] table = this.table;
\r
437 Slot [] newTable = null;
\r
438 int cap = Capacity;
\r
439 bool gap = (free >=0 && free < Count);
\r
442 newTable = new Slot [n << 1];
\r
445 if (newTable != null) {
\r
447 int copyLen = free;
\r
449 Array.Copy (table, 0, newTable, 0, copyLen);
\r
451 copyLen = Count - free;
\r
453 Array.Copy (table, free, newTable, free+1, copyLen);
\r
456 // Just a resizing, copy the entire table.
\r
457 Array.Copy (table, newTable, Count);
\r
459 this.table = newTable;
\r
461 Array.Copy (table, free, table, free+1, Count - free);
\r
466 private void PutImpl (object key, object value, bool overwrite)
\r
469 throw new ArgumentNullException ("null key");
\r
471 Slot [] table = this.table;
\r
476 freeIndx = Find (key);
\r
477 } catch (Exception) {
\r
478 throw new InvalidOperationException();
\r
481 if (freeIndx >= 0) {
\r
483 throw new ArgumentException("element already exists");
\r
485 table [freeIndx].value = value;
\r
489 freeIndx = ~freeIndx;
\r
491 if (freeIndx > Capacity + 1)
\r
492 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
\r
495 EnsureCapacity (Count+1, freeIndx);
\r
497 table = this.table;
\r
498 table [freeIndx].key = key;
\r
499 table [freeIndx].value = value;
\r
502 ++modificationCount;
\r
507 private object GetImpl (object key)
\r
509 int i = Find (key);
\r
512 return table [i].value;
\r
517 private void InitTable (int capacity)
\r
519 InitTable (capacity, false);
\r
522 private void InitTable (int capacity, bool forceSize) {
\r
523 if (!forceSize && (capacity < INITIAL_SIZE)) capacity = INITIAL_SIZE;
\r
524 this.table = new Slot [capacity];
\r
526 this.modificationCount = 0;
\r
529 private void CopyToArray (Array arr, int i,
\r
530 EnumeratorMode mode)
\r
533 throw new ArgumentNullException ("arr");
\r
535 if (i < 0 || i + this.Count > arr.Length)
\r
536 throw new ArgumentOutOfRangeException ("i");
\r
538 IEnumerator it = new Enumerator (this, mode);
\r
540 while (it.MoveNext ()) {
\r
541 arr.SetValue (it.Current, i++);
\r
546 private int Find (object key)
\r
548 Slot [] table = this.table;
\r
551 if (len == 0) return ~0;
\r
553 IComparer comparer = (this.comparer == null)
\r
560 while (left <= right) {
\r
561 int guess = (left + right) >> 1;
\r
563 int cmp = comparer.Compare (key, table[guess].key);
\r
564 if (cmp == 0) return guess;
\r
566 if (cmp > 0) left = guess+1;
\r
567 else right = guess-1;
\r
580 private sealed class Enumerator : IDictionaryEnumerator,
\r
583 private SortedList host;
\r
587 private EnumeratorMode mode;
\r
589 private object currentKey;
\r
590 private object currentValue;
\r
592 bool invalid = false;
\r
594 private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";
\r
596 public Enumerator (SortedList host, EnumeratorMode mode)
\r
599 stamp = host.modificationCount;
\r
605 public Enumerator (SortedList host)
\r
606 : this (host, EnumeratorMode.ENTRY_MODE)
\r
610 public void Reset ()
\r
612 if (host.modificationCount != stamp || invalid)
\r
613 throw new InvalidOperationException (xstr);
\r
617 currentValue = null;
\r
620 public bool MoveNext ()
\r
622 if (host.modificationCount != stamp || invalid)
\r
623 throw new InvalidOperationException (xstr);
\r
625 Slot [] table = host.table;
\r
627 if (++pos < size) {
\r
628 Slot entry = table [pos];
\r
630 currentKey = entry.key;
\r
631 currentValue = entry.value;
\r
636 currentValue = null;
\r
640 public DictionaryEntry Entry
\r
643 if (invalid || pos >= size || pos == -1)
\r
644 throw new InvalidOperationException (xstr);
\r
646 return new DictionaryEntry (currentKey,
\r
651 public Object Key {
\r
653 if (invalid || pos >= size || pos == -1)
\r
654 throw new InvalidOperationException (xstr);
\r
659 public Object Value {
\r
661 if (invalid || pos >= size || pos == -1)
\r
662 throw new InvalidOperationException (xstr);
\r
663 return currentValue;
\r
667 public Object Current {
\r
669 if (invalid || pos >= size || pos == -1)
\r
670 throw new InvalidOperationException (xstr);
\r
673 case EnumeratorMode.KEY_MODE:
\r
675 case EnumeratorMode.VALUE_MODE:
\r
676 return currentValue;
\r
677 case EnumeratorMode.ENTRY_MODE:
\r
681 throw new NotSupportedException (mode + " is not a supported mode.");
\r
688 private class ListKeys : IList, IEnumerable {
\r
690 private SortedList host;
\r
693 public ListKeys (SortedList host)
\r
696 throw new ArgumentNullException ();
\r
705 public virtual int Count {
\r
711 public virtual bool IsSynchronized {
\r
713 return host.IsSynchronized;
\r
717 public virtual Object SyncRoot {
\r
719 return host.SyncRoot;
\r
723 public virtual void CopyTo (Array array, int arrayIndex)
\r
725 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
\r
733 public virtual bool IsFixedSize {
\r
739 public virtual bool IsReadOnly {
\r
746 public virtual object this [int index] {
\r
748 return host.GetKey (index);
\r
751 throw new NotSupportedException("attempt to modify a key");
\r
755 public virtual int Add (object value)
\r
757 throw new NotSupportedException("IList::Add not supported");
\r
760 public virtual void Clear ()
\r
762 throw new NotSupportedException("IList::Clear not supported");
\r
765 public virtual bool Contains (object key)
\r
767 return host.Contains (key);
\r
771 public virtual int IndexOf (object key)
\r
773 return host.IndexOfKey (key);
\r
777 public virtual void Insert (int index, object value)
\r
779 throw new NotSupportedException("IList::Insert not supported");
\r
783 public virtual void Remove (object value)
\r
785 throw new NotSupportedException("IList::Remove not supported");
\r
789 public virtual void RemoveAt (int index)
\r
791 throw new NotSupportedException("IList::RemoveAt not supported");
\r
799 public virtual IEnumerator GetEnumerator ()
\r
801 return new SortedList.Enumerator (host, EnumeratorMode.KEY_MODE);
\r
808 private class ListValues : IList, IEnumerable {
\r
810 private SortedList host;
\r
813 public ListValues (SortedList host)
\r
816 throw new ArgumentNullException ();
\r
825 public virtual int Count {
\r
831 public virtual bool IsSynchronized {
\r
833 return host.IsSynchronized;
\r
837 public virtual Object SyncRoot {
\r
839 return host.SyncRoot;
\r
843 public virtual void CopyTo (Array array, int arrayIndex)
\r
845 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
\r
853 public virtual bool IsFixedSize {
\r
859 public virtual bool IsReadOnly {
\r
867 public virtual object this [int index] {
\r
869 return host.GetByIndex (index);
\r
872 // FIXME: It seems (according to tests)
\r
873 // that modifications are allowed
\r
875 // ? host.SetByIndex (index, value);
\r
876 throw new NotSupportedException("attempt to modify a value");
\r
880 public virtual int Add (object value)
\r
882 throw new NotSupportedException("IList::Add not supported");
\r
885 public virtual void Clear ()
\r
887 throw new NotSupportedException("IList::Clear not supported");
\r
890 public virtual bool Contains (object value)
\r
892 return host.ContainsValue (value);
\r
896 public virtual int IndexOf (object value)
\r
898 return host.IndexOfValue (value);
\r
902 public virtual void Insert (int index, object value)
\r
904 throw new NotSupportedException("IList::Insert not supported");
\r
908 public virtual void Remove (object value)
\r
910 throw new NotSupportedException("IList::Remove not supported");
\r
914 public virtual void RemoveAt (int index)
\r
916 throw new NotSupportedException("IList::RemoveAt not supported");
\r
924 public virtual IEnumerator GetEnumerator ()
\r
926 return new SortedList.Enumerator (host, EnumeratorMode.VALUE_MODE);
\r
931 private class SynchedSortedList : SortedList {
\r
933 private SortedList host;
\r
935 public SynchedSortedList (SortedList host)
\r
938 throw new ArgumentNullException ();
\r
944 public override int Count {
\r
950 public override bool IsSynchronized {
\r
956 public override Object SyncRoot {
\r
958 return host.SyncRoot;
\r
966 public override bool IsFixedSize {
\r
968 return host.IsFixedSize;
\r
973 public override bool IsReadOnly {
\r
975 return host.IsReadOnly;
\r
979 public override ICollection Keys {
\r
981 ICollection keys = null;
\r
982 lock (host.SyncRoot) {
\r
989 public override ICollection Values {
\r
991 ICollection vals = null;
\r
992 lock (host.SyncRoot) {
\r
993 vals = host.Values;
\r
1001 public override Object this [object key] {
\r
1003 lock (host.SyncRoot) {
\r
1004 return host.GetImpl (key);
\r
1008 lock (host.SyncRoot) {
\r
1009 host.PutImpl (key, value, true);
\r
1018 public override void CopyTo (Array array, int arrayIndex)
\r
1020 lock (host.SyncRoot) {
\r
1021 host.CopyTo (array, arrayIndex);
\r
1028 public override void Add (object key, object value)
\r
1030 lock (host.SyncRoot) {
\r
1031 host.PutImpl (key, value, false);
\r
1035 public override void Clear ()
\r
1037 lock (host.SyncRoot) {
\r
1042 public override bool Contains (object key)
\r
1044 lock (host.SyncRoot) {
\r
1045 return (host.Find (key) >= 0);
\r
1049 public override IDictionaryEnumerator GetEnumerator ()
\r
1051 lock (host.SyncRoot) {
\r
1052 return host.GetEnumerator();
\r
1056 public override void Remove (object key)
\r
1058 lock (host.SyncRoot) {
\r
1059 host.Remove (key);
\r
1065 public override bool ContainsKey (object key)
\r
1067 lock (host.SyncRoot) {
\r
1068 return host.Contains (key);
\r
1072 public override bool ContainsValue (object value)
\r
1074 lock (host.SyncRoot) {
\r
1075 return host.ContainsValue (value);
\r
1082 public override object Clone ()
\r
1084 lock (host.SyncRoot) {
\r
1085 return (host.Clone () as SortedList);
\r
1092 // SortedList overrides
\r
1095 public override Object GetByIndex (int index)
\r
1097 lock (host.SyncRoot) {
\r
1098 return host.GetByIndex (index);
\r
1102 public override Object GetKey (int index)
\r
1104 lock (host.SyncRoot) {
\r
1105 return host.GetKey (index);
\r
1109 public override IList GetKeyList ()
\r
1111 lock (host.SyncRoot) {
\r
1112 return new ListKeys (host);
\r
1117 public override IList GetValueList ()
\r
1119 lock (host.SyncRoot) {
\r
1120 return new ListValues (host);
\r
1124 public override void RemoveAt (int index)
\r
1126 lock (host.SyncRoot) {
\r
1127 host.RemoveAt (index);
\r
1131 public override int IndexOfKey (object key)
\r
1133 lock (host.SyncRoot) {
\r
1134 return host.IndexOfKey (key);
\r
1138 public override int IndexOfValue (Object val)
\r
1140 lock (host.SyncRoot) {
\r
1141 return host.IndexOfValue (val);
\r
1145 public override void SetByIndex (int index, object value)
\r
1147 lock (host.SyncRoot) {
\r
1148 host.SetByIndex (index, value);
\r
1152 public override void TrimToSize()
\r
1154 lock (host.SyncRoot) {
\r
1155 host.TrimToSize();
\r
1160 } // SynchedSortedList
\r
1164 } // System.Collections