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
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
\r
13 // Permission is hereby granted, free of charge, to any person obtaining
\r
14 // a copy of this software and associated documentation files (the
\r
15 // "Software"), to deal in the Software without restriction, including
\r
16 // without limitation the rights to use, copy, modify, merge, publish,
\r
17 // distribute, sublicense, and/or sell copies of the Software, and to
\r
18 // permit persons to whom the Software is furnished to do so, subject to
\r
19 // the following conditions:
\r
21 // The above copyright notice and this permission notice shall be
\r
22 // included in all copies or substantial portions of the Software.
\r
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
35 using System.Collections;
\r
36 using System.Globalization;
\r
38 namespace System.Collections {
\r
41 /// Represents a collection of associated keys and values
\r
42 /// that are sorted by the keys and are accessible by key
\r
46 [MonoTODO ("Fix serialization compatibility with MS.NET")]
\r
47 public class SortedList : IDictionary, ICollection,
\r
48 IEnumerable, ICloneable {
\r
52 internal struct Slot {
\r
53 internal Object key;
\r
54 internal Object value;
\r
57 private readonly static int INITIAL_SIZE = 16;
\r
59 private enum EnumeratorMode : int { KEY_MODE = 0, VALUE_MODE, ENTRY_MODE }
\r
62 private int modificationCount;
\r
63 private Slot[] table;
\r
64 private IComparer comparer;
\r
65 private int defaultCapacity;
\r
70 public SortedList ()
\r
71 : this (null, INITIAL_SIZE)
\r
75 public SortedList (int initialCapacity)
\r
76 : this (null, initialCapacity)
\r
80 public SortedList (IComparer comparer, int initialCapacity)
\r
82 if (initialCapacity < 0)
\r
83 throw new ArgumentOutOfRangeException ("initialCapacity");
\r
85 if (initialCapacity == 0)
\r
86 defaultCapacity = 0;
\r
88 defaultCapacity = INITIAL_SIZE;
\r
90 this.comparer = comparer;
\r
91 InitTable (initialCapacity, true);
\r
94 public SortedList (IComparer comparer)
\r
96 this.comparer = comparer;
\r
97 InitTable (INITIAL_SIZE, true);
\r
101 public SortedList (IDictionary d) : this (d, null)
\r
105 public SortedList (IDictionary d, IComparer comparer)
\r
108 throw new ArgumentNullException ("dictionary");
\r
110 InitTable (d.Count, true);
\r
111 this.comparer = comparer;
\r
113 IDictionaryEnumerator it = d.GetEnumerator ();
\r
114 while (it.MoveNext ()) {
\r
115 if (it.Key is IComparable) {
\r
116 Add (it.Key, it.Value);
\r
118 throw new InvalidCastException("!IComparable");
\r
129 public virtual int Count {
\r
135 public virtual bool IsSynchronized {
\r
141 public virtual Object SyncRoot {
\r
150 public virtual bool IsFixedSize {
\r
157 public virtual bool IsReadOnly {
\r
163 public virtual ICollection Keys {
\r
165 return new ListKeys (this);
\r
169 public virtual ICollection Values {
\r
171 return new ListValues (this);
\r
177 public virtual Object this [Object key] {
\r
180 throw new ArgumentNullException();
\r
181 return GetImpl (key);
\r
185 throw new ArgumentNullException();
\r
187 throw new NotSupportedException("SortedList is Read Only.");
\r
188 if (Find(key) < 0 && IsFixedSize)
\r
189 throw new NotSupportedException("Key not found and SortedList is fixed size.");
\r
191 PutImpl (key, value, true);
\r
195 public virtual int Capacity {
\r
197 return table.Length;
\r
201 int current = this.table.Length;
\r
203 if (inUse > value) {
\r
204 throw new ArgumentOutOfRangeException("capacity too small");
\r
206 else if (value == 0) {
\r
207 // return to default size
\r
208 Slot [] newTable = new Slot [defaultCapacity];
\r
209 Array.Copy (table, newTable, inUse);
\r
210 this.table = newTable;
\r
213 else if (current > defaultCapacity && value < current) {
\r
214 Slot [] newTable = new Slot [defaultCapacity];
\r
215 Array.Copy (table, newTable, inUse);
\r
216 this.table = newTable;
\r
219 else if (value > inUse) {
\r
220 Slot [] newTable = new Slot [value];
\r
221 Array.Copy (table, newTable, inUse);
\r
222 this.table = newTable;
\r
224 else if (value > current) {
\r
225 Slot [] newTable = new Slot [value];
\r
226 Array.Copy (table, newTable, current);
\r
227 this.table = newTable;
\r
233 // Public instance methods.
\r
238 IEnumerator IEnumerable.GetEnumerator ()
\r
240 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
\r
246 public virtual void Add (object key, object value)
\r
248 PutImpl (key, value, false);
\r
252 public virtual void Clear ()
\r
254 defaultCapacity = INITIAL_SIZE;
\r
255 this.table = new Slot [defaultCapacity];
\r
257 modificationCount++;
\r
260 public virtual bool Contains (object key)
\r
263 throw new ArgumentNullException();
\r
266 return (Find (key) >= 0);
\r
267 } catch (Exception) {
\r
268 throw new InvalidOperationException();
\r
273 public virtual IDictionaryEnumerator GetEnumerator ()
\r
275 return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
\r
278 public virtual void Remove (object key)
\r
280 int i = IndexOfKey (key);
\r
281 if (i >= 0) RemoveAt (i);
\r
287 public virtual void CopyTo (Array array, int arrayIndex)
\r
290 throw new ArgumentNullException();
\r
292 if (arrayIndex < 0)
\r
293 throw new ArgumentOutOfRangeException();
\r
295 if (array.Rank > 1)
\r
296 throw new ArgumentException("array is multi-dimensional");
\r
297 if (arrayIndex >= array.Length)
\r
298 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
\r
299 if (Count > (array.Length - arrayIndex))
\r
300 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
\r
302 IDictionaryEnumerator it = GetEnumerator ();
\r
303 int i = arrayIndex;
\r
305 while (it.MoveNext ()) {
\r
306 array.SetValue (it.Entry, i++);
\r
314 public virtual object Clone ()
\r
316 SortedList sl = new SortedList (this, comparer);
\r
317 sl.modificationCount = this.modificationCount;
\r
328 public virtual IList GetKeyList ()
\r
330 return new ListKeys (this);
\r
334 public virtual IList GetValueList ()
\r
336 return new ListValues (this);
\r
340 public virtual void RemoveAt (int index)
\r
342 Slot [] table = this.table;
\r
344 if (index >= 0 && index < cnt) {
\r
345 if (index != cnt - 1) {
\r
346 Array.Copy (table, index+1, table, index, cnt-1-index);
\r
348 table [index].key = null;
\r
349 table [index].value = null;
\r
352 ++modificationCount;
\r
354 throw new ArgumentOutOfRangeException("index out of range");
\r
358 public virtual int IndexOfKey (object key)
\r
361 throw new ArgumentNullException();
\r
366 } catch (Exception) {
\r
367 throw new InvalidOperationException();
\r
370 return (indx | (indx >> 31));
\r
374 public virtual int IndexOfValue (object value)
\r
379 for (int i = 0; i < inUse; i ++) {
\r
380 Slot current = this.table [i];
\r
382 if (Equals (value, current.value))
\r
390 public virtual bool ContainsKey (object key)
\r
393 throw new ArgumentNullException();
\r
396 return Contains (key);
\r
397 } catch (Exception) {
\r
398 throw new InvalidOperationException();
\r
403 public virtual bool ContainsValue (object value)
\r
405 return IndexOfValue (value) >= 0;
\r
409 public virtual object GetByIndex (int index)
\r
411 if (index >= 0 && index < Count)
\r
412 return table [index].value;
\r
415 throw new ArgumentOutOfRangeException("index out of range");
\r
419 public virtual void SetByIndex (int index, object value)
\r
421 if (index >= 0 && index < Count)
\r
422 table [index].value = value;
\r
425 throw new ArgumentOutOfRangeException("index out of range");
\r
429 public virtual object GetKey (int index)
\r
431 if (index >= 0 && index < Count)
\r
432 return table [index].key;
\r
435 throw new ArgumentOutOfRangeException("index out of range");
\r
438 public static SortedList Synchronized (SortedList list)
\r
441 throw new ArgumentNullException (Locale.GetText ("Base list is null."));
\r
443 return new SynchedSortedList (list);
\r
446 public virtual void TrimToSize ()
\r
449 // Trimming an empty SortedList sets the capacity
\r
450 // of the SortedList to the default capacity,
\r
453 Resize (defaultCapacity, false);
\r
455 Resize (Count, true);
\r
464 private void Resize (int n, bool copy)
\r
466 Slot [] table = this.table;
\r
467 Slot [] newTable = new Slot [n];
\r
468 if (copy) Array.Copy (table, 0, newTable, 0, n);
\r
469 this.table = newTable;
\r
473 private void EnsureCapacity (int n, int free)
\r
475 Slot [] table = this.table;
\r
476 Slot [] newTable = null;
\r
477 int cap = Capacity;
\r
478 bool gap = (free >=0 && free < Count);
\r
481 newTable = new Slot [n << 1];
\r
484 if (newTable != null) {
\r
486 int copyLen = free;
\r
488 Array.Copy (table, 0, newTable, 0, copyLen);
\r
490 copyLen = Count - free;
\r
492 Array.Copy (table, free, newTable, free+1, copyLen);
\r
495 // Just a resizing, copy the entire table.
\r
496 Array.Copy (table, newTable, Count);
\r
498 this.table = newTable;
\r
500 Array.Copy (table, free, table, free+1, Count - free);
\r
505 private void PutImpl (object key, object value, bool overwrite)
\r
508 throw new ArgumentNullException ("null key");
\r
510 Slot [] table = this.table;
\r
515 freeIndx = Find (key);
\r
516 } catch (Exception) {
\r
517 throw new InvalidOperationException();
\r
520 if (freeIndx >= 0) {
\r
522 throw new ArgumentException("element already exists");
\r
524 table [freeIndx].value = value;
\r
528 freeIndx = ~freeIndx;
\r
530 if (freeIndx > Capacity + 1)
\r
531 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
\r
534 EnsureCapacity (Count+1, freeIndx);
\r
536 table = this.table;
\r
537 table [freeIndx].key = key;
\r
538 table [freeIndx].value = value;
\r
541 ++modificationCount;
\r
546 private object GetImpl (object key)
\r
548 int i = Find (key);
\r
551 return table [i].value;
\r
556 private void InitTable (int capacity)
\r
558 InitTable (capacity, false);
\r
561 private void InitTable (int capacity, bool forceSize)
\r
563 if (!forceSize && (capacity < defaultCapacity))
\r
564 capacity = defaultCapacity;
\r
565 this.table = new Slot [capacity];
\r
567 this.modificationCount = 0;
\r
570 private void CopyToArray (Array arr, int i,
\r
571 EnumeratorMode mode)
\r
574 throw new ArgumentNullException ("arr");
\r
576 if (i < 0 || i + this.Count > arr.Length)
\r
577 throw new ArgumentOutOfRangeException ("i");
\r
579 IEnumerator it = new Enumerator (this, mode);
\r
581 while (it.MoveNext ()) {
\r
582 arr.SetValue (it.Current, i++);
\r
587 private int Find (object key)
\r
589 Slot [] table = this.table;
\r
592 if (len == 0) return ~0;
\r
594 IComparer comparer = (this.comparer == null)
\r
601 while (left <= right) {
\r
602 int guess = (left + right) >> 1;
\r
604 int cmp = comparer.Compare (key, table[guess].key);
\r
605 if (cmp == 0) return guess;
\r
607 if (cmp > 0) left = guess+1;
\r
608 else right = guess-1;
\r
621 private sealed class Enumerator : ICloneable, IDictionaryEnumerator, IEnumerator {
\r
623 private SortedList host;
\r
627 private EnumeratorMode mode;
\r
629 private object currentKey;
\r
630 private object currentValue;
\r
632 bool invalid = false;
\r
634 private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";
\r
636 public Enumerator (SortedList host, EnumeratorMode mode)
\r
639 stamp = host.modificationCount;
\r
645 public Enumerator (SortedList host)
\r
646 : this (host, EnumeratorMode.ENTRY_MODE)
\r
650 public void Reset ()
\r
652 if (host.modificationCount != stamp || invalid)
\r
653 throw new InvalidOperationException (xstr);
\r
657 currentValue = null;
\r
660 public bool MoveNext ()
\r
662 if (host.modificationCount != stamp || invalid)
\r
663 throw new InvalidOperationException (xstr);
\r
665 Slot [] table = host.table;
\r
667 if (++pos < size) {
\r
668 Slot entry = table [pos];
\r
670 currentKey = entry.key;
\r
671 currentValue = entry.value;
\r
676 currentValue = null;
\r
680 public DictionaryEntry Entry
\r
683 if (invalid || pos >= size || pos == -1)
\r
684 throw new InvalidOperationException (xstr);
\r
686 return new DictionaryEntry (currentKey,
\r
691 public Object Key {
\r
693 if (invalid || pos >= size || pos == -1)
\r
694 throw new InvalidOperationException (xstr);
\r
699 public Object Value {
\r
701 if (invalid || pos >= size || pos == -1)
\r
702 throw new InvalidOperationException (xstr);
\r
703 return currentValue;
\r
707 public Object Current {
\r
709 if (invalid || pos >= size || pos == -1)
\r
710 throw new InvalidOperationException (xstr);
\r
713 case EnumeratorMode.KEY_MODE:
\r
715 case EnumeratorMode.VALUE_MODE:
\r
716 return currentValue;
\r
717 case EnumeratorMode.ENTRY_MODE:
\r
721 throw new NotSupportedException (mode + " is not a supported mode.");
\r
728 public object Clone ()
\r
730 Enumerator e = new Enumerator (host, mode);
\r
734 e.currentKey = currentKey;
\r
735 e.currentValue = currentValue;
\r
736 e.invalid = invalid;
\r
742 private class ListKeys : IList, IEnumerable {
\r
744 private SortedList host;
\r
747 public ListKeys (SortedList host)
\r
750 throw new ArgumentNullException ();
\r
759 public virtual int Count {
\r
765 public virtual bool IsSynchronized {
\r
767 return host.IsSynchronized;
\r
771 public virtual Object SyncRoot {
\r
773 return host.SyncRoot;
\r
777 public virtual void CopyTo (Array array, int arrayIndex)
\r
779 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
\r
787 public virtual bool IsFixedSize {
\r
793 public virtual bool IsReadOnly {
\r
800 public virtual object this [int index] {
\r
802 return host.GetKey (index);
\r
805 throw new NotSupportedException("attempt to modify a key");
\r
809 public virtual int Add (object value)
\r
811 throw new NotSupportedException("IList::Add not supported");
\r
814 public virtual void Clear ()
\r
816 throw new NotSupportedException("IList::Clear not supported");
\r
819 public virtual bool Contains (object key)
\r
821 return host.Contains (key);
\r
825 public virtual int IndexOf (object key)
\r
827 return host.IndexOfKey (key);
\r
831 public virtual void Insert (int index, object value)
\r
833 throw new NotSupportedException("IList::Insert not supported");
\r
837 public virtual void Remove (object value)
\r
839 throw new NotSupportedException("IList::Remove not supported");
\r
843 public virtual void RemoveAt (int index)
\r
845 throw new NotSupportedException("IList::RemoveAt not supported");
\r
853 public virtual IEnumerator GetEnumerator ()
\r
855 return new SortedList.Enumerator (host, EnumeratorMode.KEY_MODE);
\r
862 private class ListValues : IList, IEnumerable {
\r
864 private SortedList host;
\r
867 public ListValues (SortedList host)
\r
870 throw new ArgumentNullException ();
\r
879 public virtual int Count {
\r
885 public virtual bool IsSynchronized {
\r
887 return host.IsSynchronized;
\r
891 public virtual Object SyncRoot {
\r
893 return host.SyncRoot;
\r
897 public virtual void CopyTo (Array array, int arrayIndex)
\r
899 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
\r
907 public virtual bool IsFixedSize {
\r
913 public virtual bool IsReadOnly {
\r
921 public virtual object this [int index] {
\r
923 return host.GetByIndex (index);
\r
926 // FIXME: It seems (according to tests)
\r
927 // that modifications are allowed
\r
929 // ? host.SetByIndex (index, value);
\r
930 throw new NotSupportedException("attempt to modify a value");
\r
934 public virtual int Add (object value)
\r
936 throw new NotSupportedException("IList::Add not supported");
\r
939 public virtual void Clear ()
\r
941 throw new NotSupportedException("IList::Clear not supported");
\r
944 public virtual bool Contains (object value)
\r
946 return host.ContainsValue (value);
\r
950 public virtual int IndexOf (object value)
\r
952 return host.IndexOfValue (value);
\r
956 public virtual void Insert (int index, object value)
\r
958 throw new NotSupportedException("IList::Insert not supported");
\r
962 public virtual void Remove (object value)
\r
964 throw new NotSupportedException("IList::Remove not supported");
\r
968 public virtual void RemoveAt (int index)
\r
970 throw new NotSupportedException("IList::RemoveAt not supported");
\r
978 public virtual IEnumerator GetEnumerator ()
\r
980 return new SortedList.Enumerator (host, EnumeratorMode.VALUE_MODE);
\r
985 private class SynchedSortedList : SortedList {
\r
987 private SortedList host;
\r
989 public SynchedSortedList (SortedList host)
\r
992 throw new ArgumentNullException ();
\r
996 public override int Capacity {
\r
998 lock (host.SyncRoot) {
\r
999 return host.Capacity;
\r
1003 lock (host.SyncRoot) {
\r
1004 host.Capacity = value;
\r
1011 public override int Count {
\r
1013 return host.Count;
\r
1017 public override bool IsSynchronized {
\r
1023 public override Object SyncRoot {
\r
1025 return host.SyncRoot;
\r
1033 public override bool IsFixedSize {
\r
1035 return host.IsFixedSize;
\r
1040 public override bool IsReadOnly {
\r
1042 return host.IsReadOnly;
\r
1046 public override ICollection Keys {
\r
1048 ICollection keys = null;
\r
1049 lock (host.SyncRoot) {
\r
1056 public override ICollection Values {
\r
1058 ICollection vals = null;
\r
1059 lock (host.SyncRoot) {
\r
1060 vals = host.Values;
\r
1068 public override Object this [object key] {
\r
1070 lock (host.SyncRoot) {
\r
1071 return host.GetImpl (key);
\r
1075 lock (host.SyncRoot) {
\r
1076 host.PutImpl (key, value, true);
\r
1085 public override void CopyTo (Array array, int arrayIndex)
\r
1087 lock (host.SyncRoot) {
\r
1088 host.CopyTo (array, arrayIndex);
\r
1095 public override void Add (object key, object value)
\r
1097 lock (host.SyncRoot) {
\r
1098 host.PutImpl (key, value, false);
\r
1102 public override void Clear ()
\r
1104 lock (host.SyncRoot) {
\r
1109 public override bool Contains (object key)
\r
1111 lock (host.SyncRoot) {
\r
1112 return (host.Find (key) >= 0);
\r
1116 public override IDictionaryEnumerator GetEnumerator ()
\r
1118 lock (host.SyncRoot) {
\r
1119 return host.GetEnumerator();
\r
1123 public override void Remove (object key)
\r
1125 lock (host.SyncRoot) {
\r
1126 host.Remove (key);
\r
1132 public override bool ContainsKey (object key)
\r
1134 lock (host.SyncRoot) {
\r
1135 return host.Contains (key);
\r
1139 public override bool ContainsValue (object value)
\r
1141 lock (host.SyncRoot) {
\r
1142 return host.ContainsValue (value);
\r
1149 public override object Clone ()
\r
1151 lock (host.SyncRoot) {
\r
1152 return (host.Clone () as SortedList);
\r
1159 // SortedList overrides
\r
1162 public override Object GetByIndex (int index)
\r
1164 lock (host.SyncRoot) {
\r
1165 return host.GetByIndex (index);
\r
1169 public override Object GetKey (int index)
\r
1171 lock (host.SyncRoot) {
\r
1172 return host.GetKey (index);
\r
1176 public override IList GetKeyList ()
\r
1178 lock (host.SyncRoot) {
\r
1179 return new ListKeys (host);
\r
1184 public override IList GetValueList ()
\r
1186 lock (host.SyncRoot) {
\r
1187 return new ListValues (host);
\r
1191 public override void RemoveAt (int index)
\r
1193 lock (host.SyncRoot) {
\r
1194 host.RemoveAt (index);
\r
1198 public override int IndexOfKey (object key)
\r
1200 lock (host.SyncRoot) {
\r
1201 return host.IndexOfKey (key);
\r
1205 public override int IndexOfValue (Object val)
\r
1207 lock (host.SyncRoot) {
\r
1208 return host.IndexOfValue (val);
\r
1212 public override void SetByIndex (int index, object value)
\r
1214 lock (host.SyncRoot) {
\r
1215 host.SetByIndex (index, value);
\r
1219 public override void TrimToSize()
\r
1221 lock (host.SyncRoot) {
\r
1222 host.TrimToSize();
\r
1227 } // SynchedSortedList
\r
1231 } // System.Collections
\r