2 // System.Collections.SortedList
\r
5 // Sergey Chaban (serge@wildwestsoftware.com)
\r
10 using System.Collections;
\r
13 namespace System.Collections {
\r
16 /// Represents a collection of associated keys and values
\r
17 /// that are sorted by the keys and are accessible by key
\r
21 public class SortedList : IDictionary, ICollection,
\r
22 IEnumerable, ICloneable {
\r
25 internal struct Slot {
\r
26 internal Object key;
\r
27 internal Object value;
\r
30 private readonly static int INITIAL_SIZE = 16;
\r
32 public enum EnumeratorMode : int {KEY_MODE = 0, VALUE_MODE}
\r
35 private int modificationCount;
\r
36 private Slot[] table;
\r
37 private IComparer comparer;
\r
44 public SortedList () : this (INITIAL_SIZE)
\r
48 public SortedList (int initialCapacity)
\r
49 : this (null, initialCapacity)
\r
53 public SortedList (IComparer comparer, int initialCapacity)
\r
55 this.comparer = comparer;
\r
56 InitTable (initialCapacity);
\r
59 public SortedList (IComparer comparer)
\r
60 : this (comparer, 0)
\r
65 public SortedList (IDictionary d) : this (d, null)
\r
69 public SortedList (IDictionary d, IComparer comparer)
\r
72 throw new ArgumentNullException ("dictionary");
\r
74 InitTable (d.Count);
\r
75 this.comparer = comparer;
\r
77 IDictionaryEnumerator it = d.GetEnumerator ();
\r
78 while (it.MoveNext ()) {
\r
79 if (it.Key is IComparable) {
\r
80 Add (it.Key, it.Value);
\r
82 throw new InvalidCastException("!IComparable");
\r
97 public virtual int Count {
\r
103 public virtual bool IsSynchronized {
\r
109 public virtual Object SyncRoot {
\r
118 public virtual bool IsFixedSize {
\r
125 public virtual bool IsReadOnly {
\r
131 public virtual ICollection Keys {
\r
133 return new ListKeys (this);
\r
137 public virtual ICollection Values {
\r
139 return new ListValues (this);
\r
145 public virtual Object this [Object key] {
\r
147 return GetImpl (key);
\r
150 PutImpl (key, value, true);
\r
157 public virtual int Capacity {
\r
159 return table.Length;
\r
162 Slot [] table = this.table;
\r
163 int current = table.Length;
\r
166 throw new ArgumentOutOfRangeException("capacity too small");
\r
168 if (value > current) {
\r
169 Slot [] newTable = new Slot [value];
\r
170 Array.Copy (table, newTable, current);
\r
171 this.table = newTable;
\r
179 // Public instance methods.
\r
185 IEnumerator IEnumerable.GetEnumerator ()
\r
187 return new Enumerator (this, EnumeratorMode.KEY_MODE);
\r
193 public virtual void Add (object key, object value)
\r
195 PutImpl (key, value, false);
\r
199 public virtual void Clear ()
\r
201 this.table = new Slot [Capacity];
\r
203 modificationCount++;
\r
206 public virtual bool Contains (object key)
\r
208 return (Find (key) >= 0);
\r
212 public virtual IDictionaryEnumerator GetEnumerator ()
\r
214 return new Enumerator (this, EnumeratorMode.KEY_MODE);
\r
217 public virtual void Remove (object key)
\r
219 int i = IndexOfKey (key);
\r
220 if (i >= 0) RemoveAt (i);
\r
226 public virtual void CopyTo (Array array, int arrayIndex)
\r
228 IDictionaryEnumerator it = GetEnumerator ();
\r
229 int i = arrayIndex;
\r
231 while (it.MoveNext ()) {
\r
232 array.SetValue (it.Entry, i++);
\r
240 public virtual object Clone ()
\r
242 SortedList sl = new SortedList (this, comparer);
\r
243 sl.modificationCount = this.modificationCount;
\r
254 public virtual IList GetKeyList ()
\r
256 return new ListKeys (this);
\r
260 public virtual IList GetValueList ()
\r
262 return new ListValues (this);
\r
266 public virtual void RemoveAt (int index)
\r
268 Slot [] table = this.table;
\r
270 if (index >= 0 && index < cnt) {
\r
271 if (index != cnt - 1) {
\r
272 Array.Copy (table, index+1, table, index, cnt-1-index);
\r
274 table [index].key = null;
\r
275 table [index].value = null;
\r
278 ++modificationCount;
\r
280 throw new ArgumentOutOfRangeException("index out of range");
\r
288 public virtual int IndexOfKey (object key)
\r
290 int indx = Find (key);
\r
291 return (indx | (indx >> 31));
\r
295 public virtual int IndexOfValue (object value)
\r
297 Slot [] table = this.table;
\r
298 int len = table.Length;
\r
300 for (int i=0; i < len; i++) {
\r
301 if (table[i].value.Equals (value)) {
\r
310 public virtual bool ContainsKey (object key)
\r
312 return Contains (key);
\r
316 public virtual bool ContainsValue (object value)
\r
318 return IndexOfValue (value) >= 0;
\r
322 public virtual object GetByIndex (int index)
\r
324 if (index >= 0 && index < Count) {
\r
325 return table [index].value;
\r
327 throw new ArgumentOutOfRangeException("index out of range");
\r
332 public virtual void SetByIndex (int index, object value)
\r
334 if (index >= 0 && index < Count) {
\r
335 table [index].value = value;
\r
337 throw new ArgumentOutOfRangeException("index out of range");
\r
342 public virtual object GetKey (int index)
\r
344 if (index >= 0 && index < Count) {
\r
345 return table [index].key;
\r
347 throw new ArgumentOutOfRangeException("index out of range");
\r
352 public static SortedList Synchronized (SortedList list)
\r
357 public virtual void TrimToSize ()
\r
360 // Trimming an empty SortedList sets the capacity
\r
361 // of the SortedList to the default capacity,
\r
363 if (Count == 0) Resize (INITIAL_SIZE, false);
\r
364 else Resize (Count, true);
\r
373 private void Resize (int n, bool copy)
\r
375 Slot [] table = this.table;
\r
376 Slot [] newTable = new Slot [n];
\r
377 if (copy) Array.Copy (table, 0, newTable, 0, n);
\r
378 this.table = newTable;
\r
382 private void EnsureCapacity (int n, int free)
\r
384 Slot [] table = this.table;
\r
385 Slot [] newTable = null;
\r
386 int cap = Capacity;
\r
387 bool gap = (free >=0 && free < Count);
\r
390 newTable = new Slot [n << 1];
\r
393 if (newTable != null) {
\r
395 int copyLen = free;
\r
397 Array.Copy (table, 0, newTable, 0, copyLen);
\r
399 copyLen = Count - free;
\r
401 Array.Copy (table, free, newTable, free+1, copyLen);
\r
404 // Just a resizing, copy the entire table.
\r
405 Array.Copy (table, newTable, Count);
\r
407 this.table = newTable;
\r
409 Array.Copy (table, free, table, free+1, Count - free);
\r
414 private void PutImpl (object key, object value, bool overwrite)
\r
417 throw new ArgumentNullException ("null key");
\r
419 Slot [] table = this.table;
\r
420 int freeIndx = Find (key);
\r
423 if (freeIndx >= 0) {
\r
425 throw new ArgumentException("element already exists");
\r
427 table [freeIndx].value = value;
\r
431 freeIndx = ~freeIndx;
\r
433 if (freeIndx > Capacity + 1)
\r
434 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
\r
437 EnsureCapacity (Count+1, freeIndx);
\r
439 table = this.table;
\r
440 table [freeIndx].key = key;
\r
441 table [freeIndx].value = value;
\r
444 ++modificationCount;
\r
449 private object GetImpl (object key)
\r
451 int i = Find (key);
\r
454 return table [i].value;
\r
460 private void InitTable (int capacity)
\r
462 int size = (capacity + 1) & (~1);
\r
463 if (size < INITIAL_SIZE) size = INITIAL_SIZE;
\r
464 this.table = new Slot [size];
\r
466 this.modificationCount = 0;
\r
470 private void CopyToArray (Array arr, int i,
\r
471 EnumeratorMode mode)
\r
473 IEnumerator it = new Enumerator (this, mode);
\r
475 while (it.MoveNext ()) {
\r
476 arr.SetValue (it.Current, i++);
\r
481 private int Find (object key)
\r
483 Slot [] table = this.table;
\r
486 if (len == 0) return ~0;
\r
488 IComparer comparer = (this.comparer == null)
\r
495 while (left <= right) {
\r
496 int guess = (left + right) >> 1;
\r
498 int cmp = comparer.Compare (key, table[guess].key);
\r
500 if (cmp == 0) return guess;
\r
502 cmp &= ~Int32.MaxValue;
\r
504 if (cmp == 0) left = guess+1;
\r
505 else right = guess-1;
\r
518 protected sealed class Enumerator : IDictionaryEnumerator,
\r
521 private SortedList host;
\r
525 private EnumeratorMode mode;
\r
527 private object currentKey;
\r
528 private object currentValue;
\r
530 private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";
\r
532 public Enumerator (SortedList host, EnumeratorMode mode)
\r
535 stamp = host.modificationCount;
\r
541 public Enumerator (SortedList host)
\r
542 : this (host, EnumeratorMode.KEY_MODE)
\r
547 private void FailFast ()
\r
549 if (host.modificationCount != stamp) {
\r
550 throw new InvalidOperationException (xstr);
\r
554 public void Reset ()
\r
560 currentValue = null;
\r
563 public bool MoveNext ()
\r
567 Slot [] table = host.table;
\r
569 if (++pos < size) {
\r
570 Slot entry = table [pos];
\r
572 currentKey = entry.key;
\r
573 currentValue = entry.value;
\r
578 currentValue = null;
\r
582 public DictionaryEntry Entry
\r
586 return new DictionaryEntry (currentKey,
\r
591 public Object Key {
\r
598 public Object Value {
\r
601 return currentValue;
\r
605 public Object Current {
\r
608 return (mode == EnumeratorMode.KEY_MODE)
\r
616 protected class ListKeys : IList, IEnumerable {
\r
618 private SortedList host;
\r
621 public ListKeys (SortedList host)
\r
624 throw new ArgumentNullException ();
\r
633 public virtual int Count {
\r
639 public virtual bool IsSynchronized {
\r
641 return host.IsSynchronized;
\r
645 public virtual Object SyncRoot {
\r
647 return host.SyncRoot;
\r
651 public virtual void CopyTo (Array array, int arrayIndex)
\r
653 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
\r
661 public virtual bool IsFixedSize {
\r
667 public virtual bool IsReadOnly {
\r
674 public virtual object this [int index] {
\r
676 return host.GetKey (index);
\r
679 throw new NotSupportedException("attempt to modify a key");
\r
683 public virtual int Add (object value)
\r
685 throw new NotSupportedException("IList::Add not supported");
\r
688 public virtual void Clear ()
\r
690 throw new NotSupportedException("IList::Clear not supported");
\r
693 public virtual bool Contains (object key)
\r
695 return host.Contains (key);
\r
699 public virtual int IndexOf (object key)
\r
701 return host.IndexOfKey (key);
\r
705 public virtual void Insert (int index, object value)
\r
707 throw new NotSupportedException("IList::Insert not supported");
\r
711 public virtual void Remove (object value)
\r
713 throw new NotSupportedException("IList::Remove not supported");
\r
717 public virtual void RemoveAt (int index)
\r
719 throw new NotSupportedException("IList::RemoveAt not supported");
\r
727 public virtual IEnumerator GetEnumerator ()
\r
729 return new SortedList.Enumerator (host, EnumeratorMode.KEY_MODE);
\r
736 protected class ListValues : IList, IEnumerable {
\r
738 private SortedList host;
\r
741 public ListValues (SortedList host)
\r
744 throw new ArgumentNullException ();
\r
753 public virtual int Count {
\r
759 public virtual bool IsSynchronized {
\r
761 return host.IsSynchronized;
\r
765 public virtual Object SyncRoot {
\r
767 return host.SyncRoot;
\r
771 public virtual void CopyTo (Array array, int arrayIndex)
\r
773 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
\r
781 public virtual bool IsFixedSize {
\r
787 public virtual bool IsReadOnly {
\r
795 public virtual object this [int index] {
\r
797 return host.GetByIndex (index);
\r
800 // FIXME: It seems (according to tests)
\r
801 // that modifications are allowed
\r
803 // ? host.SetByIndex (index, value);
\r
804 throw new NotSupportedException("attempt to modify a value");
\r
808 public virtual int Add (object value)
\r
810 throw new NotSupportedException("IList::Add not supported");
\r
813 public virtual void Clear ()
\r
815 throw new NotSupportedException("IList::Clear not supported");
\r
818 public virtual bool Contains (object value)
\r
820 return host.ContainsValue (value);
\r
824 public virtual int IndexOf (object value)
\r
826 return host.IndexOfValue (value);
\r
830 public virtual void Insert (int index, object value)
\r
832 throw new NotSupportedException("IList::Insert not supported");
\r
836 public virtual void Remove (object value)
\r
838 throw new NotSupportedException("IList::Remove not supported");
\r
842 public virtual void RemoveAt (int index)
\r
844 throw new NotSupportedException("IList::RemoveAt not supported");
\r
852 public virtual IEnumerator GetEnumerator ()
\r
854 return new SortedList.Enumerator (host, EnumeratorMode.VALUE_MODE);
\r
862 } // System.Collections
\r