2 // System.Collections.SortedList.cs
5 // Sergey Chaban (serge@wildwestsoftware.com)
6 // Duncan Mak (duncan@ximian.com)
7 // Herve Poussineau (hpoussineau@fr.st
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Globalization;
38 namespace System.Collections {
41 /// Represents a collection of associated keys and values
42 /// that are sorted by the keys and are accessible by key
46 [MonoTODO ("Fix serialization compatibility with MS.NET")]
47 public class SortedList : IDictionary, ICollection,
48 IEnumerable, ICloneable {
52 internal struct Slot {
54 internal Object value;
57 private readonly static int INITIAL_SIZE = 16;
59 private enum EnumeratorMode : int { KEY_MODE = 0, VALUE_MODE, ENTRY_MODE }
62 private int modificationCount;
64 private IComparer comparer;
65 private int defaultCapacity;
71 : this (null, INITIAL_SIZE)
75 public SortedList (int initialCapacity)
76 : this (null, initialCapacity)
80 public SortedList (IComparer comparer, int initialCapacity)
82 if (initialCapacity < 0)
83 throw new ArgumentOutOfRangeException ("initialCapacity");
85 if (initialCapacity == 0)
88 defaultCapacity = INITIAL_SIZE;
90 this.comparer = comparer;
91 InitTable (initialCapacity, true);
94 public SortedList (IComparer comparer)
96 this.comparer = comparer;
97 InitTable (INITIAL_SIZE, true);
101 public SortedList (IDictionary d) : this (d, null)
105 public SortedList (IDictionary d, IComparer comparer)
108 throw new ArgumentNullException ("dictionary");
110 InitTable (d.Count, true);
111 this.comparer = comparer;
113 IDictionaryEnumerator it = d.GetEnumerator ();
114 while (it.MoveNext ()) {
115 if (it.Key is IComparable) {
116 Add (it.Key, it.Value);
118 throw new InvalidCastException("!IComparable");
129 public virtual int Count {
135 public virtual bool IsSynchronized {
141 public virtual Object SyncRoot {
150 public virtual bool IsFixedSize {
157 public virtual bool IsReadOnly {
163 public virtual ICollection Keys {
165 return new ListKeys (this);
169 public virtual ICollection Values {
171 return new ListValues (this);
177 public virtual Object this [Object key] {
180 throw new ArgumentNullException();
181 return GetImpl (key);
185 throw new ArgumentNullException();
187 throw new NotSupportedException("SortedList is Read Only.");
188 if (Find(key) < 0 && IsFixedSize)
189 throw new NotSupportedException("Key not found and SortedList is fixed size.");
191 PutImpl (key, value, true);
195 public virtual int Capacity {
201 int current = this.table.Length;
204 throw new ArgumentOutOfRangeException("capacity too small");
206 else if (value == 0) {
207 // return to default size
208 Slot [] newTable = new Slot [defaultCapacity];
209 Array.Copy (table, newTable, inUse);
210 this.table = newTable;
213 else if (current > defaultCapacity && value < current) {
214 Slot [] newTable = new Slot [defaultCapacity];
215 Array.Copy (table, newTable, inUse);
216 this.table = newTable;
219 else if (value > inUse) {
220 Slot [] newTable = new Slot [value];
221 Array.Copy (table, newTable, inUse);
222 this.table = newTable;
224 else if (value > current) {
225 Slot [] newTable = new Slot [value];
226 Array.Copy (table, newTable, current);
227 this.table = newTable;
233 // Public instance methods.
238 IEnumerator IEnumerable.GetEnumerator ()
240 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
246 public virtual void Add (object key, object value)
248 PutImpl (key, value, false);
252 public virtual void Clear ()
254 defaultCapacity = INITIAL_SIZE;
255 this.table = new Slot [defaultCapacity];
260 public virtual bool Contains (object key)
263 throw new ArgumentNullException();
266 return (Find (key) >= 0);
267 } catch (Exception) {
268 throw new InvalidOperationException();
273 public virtual IDictionaryEnumerator GetEnumerator ()
275 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
278 public virtual void Remove (object key)
280 int i = IndexOfKey (key);
281 if (i >= 0) RemoveAt (i);
287 public virtual void CopyTo (Array array, int arrayIndex)
290 throw new ArgumentNullException();
293 throw new ArgumentOutOfRangeException();
296 throw new ArgumentException("array is multi-dimensional");
297 if (arrayIndex >= array.Length)
298 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
299 if (Count > (array.Length - arrayIndex))
300 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
302 IDictionaryEnumerator it = GetEnumerator ();
305 while (it.MoveNext ()) {
306 array.SetValue (it.Entry, i++);
314 public virtual object Clone ()
316 SortedList sl = new SortedList (this, comparer);
317 sl.modificationCount = this.modificationCount;
328 public virtual IList GetKeyList ()
330 return new ListKeys (this);
334 public virtual IList GetValueList ()
336 return new ListValues (this);
340 public virtual void RemoveAt (int index)
342 Slot [] table = this.table;
344 if (index >= 0 && index < cnt) {
345 if (index != cnt - 1) {
346 Array.Copy (table, index+1, table, index, cnt-1-index);
348 table [index].key = null;
349 table [index].value = null;
354 throw new ArgumentOutOfRangeException("index out of range");
358 public virtual int IndexOfKey (object key)
361 throw new ArgumentNullException();
366 } catch (Exception) {
367 throw new InvalidOperationException();
370 return (indx | (indx >> 31));
374 public virtual int IndexOfValue (object value)
379 for (int i = 0; i < inUse; i ++) {
380 Slot current = this.table [i];
382 if (Equals (value, current.value))
390 public virtual bool ContainsKey (object key)
393 throw new ArgumentNullException();
396 return Contains (key);
397 } catch (Exception) {
398 throw new InvalidOperationException();
403 public virtual bool ContainsValue (object value)
405 return IndexOfValue (value) >= 0;
409 public virtual object GetByIndex (int index)
411 if (index >= 0 && index < Count)
412 return table [index].value;
415 throw new ArgumentOutOfRangeException("index out of range");
419 public virtual void SetByIndex (int index, object value)
421 if (index >= 0 && index < Count)
422 table [index].value = value;
425 throw new ArgumentOutOfRangeException("index out of range");
429 public virtual object GetKey (int index)
431 if (index >= 0 && index < Count)
432 return table [index].key;
435 throw new ArgumentOutOfRangeException("index out of range");
438 public static SortedList Synchronized (SortedList list)
441 throw new ArgumentNullException (Locale.GetText ("Base list is null."));
443 return new SynchedSortedList (list);
446 public virtual void TrimToSize ()
449 // Trimming an empty SortedList sets the capacity
450 // of the SortedList to the default capacity,
453 Resize (defaultCapacity, false);
455 Resize (Count, true);
464 private void Resize (int n, bool copy)
466 Slot [] table = this.table;
467 Slot [] newTable = new Slot [n];
468 if (copy) Array.Copy (table, 0, newTable, 0, n);
469 this.table = newTable;
473 private void EnsureCapacity (int n, int free)
475 Slot [] table = this.table;
476 Slot [] newTable = null;
478 bool gap = (free >=0 && free < Count);
481 newTable = new Slot [n << 1];
484 if (newTable != null) {
488 Array.Copy (table, 0, newTable, 0, copyLen);
490 copyLen = Count - free;
492 Array.Copy (table, free, newTable, free+1, copyLen);
495 // Just a resizing, copy the entire table.
496 Array.Copy (table, newTable, Count);
498 this.table = newTable;
500 Array.Copy (table, free, table, free+1, Count - free);
505 private void PutImpl (object key, object value, bool overwrite)
508 throw new ArgumentNullException ("null key");
510 Slot [] table = this.table;
515 freeIndx = Find (key);
516 } catch (Exception) {
517 throw new InvalidOperationException();
522 throw new ArgumentException("element already exists");
524 table [freeIndx].value = value;
529 freeIndx = ~freeIndx;
531 if (freeIndx > Capacity + 1)
532 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
535 EnsureCapacity (Count+1, freeIndx);
538 table [freeIndx].key = key;
539 table [freeIndx].value = value;
547 private object GetImpl (object key)
552 return table [i].value;
557 private void InitTable (int capacity)
559 InitTable (capacity, false);
562 private void InitTable (int capacity, bool forceSize)
564 if (!forceSize && (capacity < defaultCapacity))
565 capacity = defaultCapacity;
566 this.table = new Slot [capacity];
568 this.modificationCount = 0;
571 private void CopyToArray (Array arr, int i,
575 throw new ArgumentNullException ("arr");
577 if (i < 0 || i + this.Count > arr.Length)
578 throw new ArgumentOutOfRangeException ("i");
580 IEnumerator it = new Enumerator (this, mode);
582 while (it.MoveNext ()) {
583 arr.SetValue (it.Current, i++);
588 private int Find (object key)
590 Slot [] table = this.table;
593 if (len == 0) return ~0;
595 IComparer comparer = (this.comparer == null)
602 while (left <= right) {
603 int guess = (left + right) >> 1;
605 int cmp = comparer.Compare (key, table[guess].key);
606 if (cmp == 0) return guess;
608 if (cmp > 0) left = guess+1;
609 else right = guess-1;
622 private sealed class Enumerator : ICloneable, IDictionaryEnumerator, IEnumerator {
624 private SortedList host;
628 private EnumeratorMode mode;
630 private object currentKey;
631 private object currentValue;
633 bool invalid = false;
635 private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";
637 public Enumerator (SortedList host, EnumeratorMode mode)
640 stamp = host.modificationCount;
646 public Enumerator (SortedList host)
647 : this (host, EnumeratorMode.ENTRY_MODE)
653 if (host.modificationCount != stamp || invalid)
654 throw new InvalidOperationException (xstr);
661 public bool MoveNext ()
663 if (host.modificationCount != stamp || invalid)
664 throw new InvalidOperationException (xstr);
666 Slot [] table = host.table;
669 Slot entry = table [pos];
671 currentKey = entry.key;
672 currentValue = entry.value;
681 public DictionaryEntry Entry
684 if (invalid || pos >= size || pos == -1)
685 throw new InvalidOperationException (xstr);
687 return new DictionaryEntry (currentKey,
694 if (invalid || pos >= size || pos == -1)
695 throw new InvalidOperationException (xstr);
700 public Object Value {
702 if (invalid || pos >= size || pos == -1)
703 throw new InvalidOperationException (xstr);
708 public Object Current {
710 if (invalid || pos >= size || pos == -1)
711 throw new InvalidOperationException (xstr);
714 case EnumeratorMode.KEY_MODE:
716 case EnumeratorMode.VALUE_MODE:
718 case EnumeratorMode.ENTRY_MODE:
722 throw new NotSupportedException (mode + " is not a supported mode.");
729 public object Clone ()
731 Enumerator e = new Enumerator (host, mode);
735 e.currentKey = currentKey;
736 e.currentValue = currentValue;
743 private class ListKeys : IList, IEnumerable {
745 private SortedList host;
748 public ListKeys (SortedList host)
751 throw new ArgumentNullException ();
760 public virtual int Count {
766 public virtual bool IsSynchronized {
768 return host.IsSynchronized;
772 public virtual Object SyncRoot {
774 return host.SyncRoot;
778 public virtual void CopyTo (Array array, int arrayIndex)
780 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
788 public virtual bool IsFixedSize {
794 public virtual bool IsReadOnly {
801 public virtual object this [int index] {
803 return host.GetKey (index);
806 throw new NotSupportedException("attempt to modify a key");
810 public virtual int Add (object value)
812 throw new NotSupportedException("IList::Add not supported");
815 public virtual void Clear ()
817 throw new NotSupportedException("IList::Clear not supported");
820 public virtual bool Contains (object key)
822 return host.Contains (key);
826 public virtual int IndexOf (object key)
828 return host.IndexOfKey (key);
832 public virtual void Insert (int index, object value)
834 throw new NotSupportedException("IList::Insert not supported");
838 public virtual void Remove (object value)
840 throw new NotSupportedException("IList::Remove not supported");
844 public virtual void RemoveAt (int index)
846 throw new NotSupportedException("IList::RemoveAt not supported");
854 public virtual IEnumerator GetEnumerator ()
856 return new SortedList.Enumerator (host, EnumeratorMode.KEY_MODE);
863 private class ListValues : IList, IEnumerable {
865 private SortedList host;
868 public ListValues (SortedList host)
871 throw new ArgumentNullException ();
880 public virtual int Count {
886 public virtual bool IsSynchronized {
888 return host.IsSynchronized;
892 public virtual Object SyncRoot {
894 return host.SyncRoot;
898 public virtual void CopyTo (Array array, int arrayIndex)
900 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
908 public virtual bool IsFixedSize {
914 public virtual bool IsReadOnly {
922 public virtual object this [int index] {
924 return host.GetByIndex (index);
927 // FIXME: It seems (according to tests)
928 // that modifications are allowed
930 // ? host.SetByIndex (index, value);
931 throw new NotSupportedException("attempt to modify a value");
935 public virtual int Add (object value)
937 throw new NotSupportedException("IList::Add not supported");
940 public virtual void Clear ()
942 throw new NotSupportedException("IList::Clear not supported");
945 public virtual bool Contains (object value)
947 return host.ContainsValue (value);
951 public virtual int IndexOf (object value)
953 return host.IndexOfValue (value);
957 public virtual void Insert (int index, object value)
959 throw new NotSupportedException("IList::Insert not supported");
963 public virtual void Remove (object value)
965 throw new NotSupportedException("IList::Remove not supported");
969 public virtual void RemoveAt (int index)
971 throw new NotSupportedException("IList::RemoveAt not supported");
979 public virtual IEnumerator GetEnumerator ()
981 return new SortedList.Enumerator (host, EnumeratorMode.VALUE_MODE);
986 private class SynchedSortedList : SortedList {
988 private SortedList host;
990 public SynchedSortedList (SortedList host)
993 throw new ArgumentNullException ();
997 public override int Capacity {
999 lock (host.SyncRoot) {
1000 return host.Capacity;
1004 lock (host.SyncRoot) {
1005 host.Capacity = value;
1012 public override int Count {
1018 public override bool IsSynchronized {
1024 public override Object SyncRoot {
1026 return host.SyncRoot;
1034 public override bool IsFixedSize {
1036 return host.IsFixedSize;
1041 public override bool IsReadOnly {
1043 return host.IsReadOnly;
1047 public override ICollection Keys {
1049 ICollection keys = null;
1050 lock (host.SyncRoot) {
1057 public override ICollection Values {
1059 ICollection vals = null;
1060 lock (host.SyncRoot) {
1069 public override Object this [object key] {
1071 lock (host.SyncRoot) {
1072 return host.GetImpl (key);
1076 lock (host.SyncRoot) {
1077 host.PutImpl (key, value, true);
1086 public override void CopyTo (Array array, int arrayIndex)
1088 lock (host.SyncRoot) {
1089 host.CopyTo (array, arrayIndex);
1096 public override void Add (object key, object value)
1098 lock (host.SyncRoot) {
1099 host.PutImpl (key, value, false);
1103 public override void Clear ()
1105 lock (host.SyncRoot) {
1110 public override bool Contains (object key)
1112 lock (host.SyncRoot) {
1113 return (host.Find (key) >= 0);
1117 public override IDictionaryEnumerator GetEnumerator ()
1119 lock (host.SyncRoot) {
1120 return host.GetEnumerator();
1124 public override void Remove (object key)
1126 lock (host.SyncRoot) {
1133 public override bool ContainsKey (object key)
1135 lock (host.SyncRoot) {
1136 return host.Contains (key);
1140 public override bool ContainsValue (object value)
1142 lock (host.SyncRoot) {
1143 return host.ContainsValue (value);
1150 public override object Clone ()
1152 lock (host.SyncRoot) {
1153 return (host.Clone () as SortedList);
1160 // SortedList overrides
1163 public override Object GetByIndex (int index)
1165 lock (host.SyncRoot) {
1166 return host.GetByIndex (index);
1170 public override Object GetKey (int index)
1172 lock (host.SyncRoot) {
1173 return host.GetKey (index);
1177 public override IList GetKeyList ()
1179 lock (host.SyncRoot) {
1180 return new ListKeys (host);
1185 public override IList GetValueList ()
1187 lock (host.SyncRoot) {
1188 return new ListValues (host);
1192 public override void RemoveAt (int index)
1194 lock (host.SyncRoot) {
1195 host.RemoveAt (index);
1199 public override int IndexOfKey (object key)
1201 lock (host.SyncRoot) {
1202 return host.IndexOfKey (key);
1206 public override int IndexOfValue (Object val)
1208 lock (host.SyncRoot) {
1209 return host.IndexOfValue (val);
1213 public override void SetByIndex (int index, object value)
1215 lock (host.SyncRoot) {
1216 host.SetByIndex (index, value);
1220 public override void TrimToSize()
1222 lock (host.SyncRoot) {
1228 } // SynchedSortedList
1232 } // System.Collections