2 // System.Collections.SortedList
\r
5 // Sergey Chaban (serge@wildwestsoftware.com)
\r
10 using System.Collections;
\r
12 namespace System.Collections {
\r
15 /// Represents a collection of associated keys and values
\r
16 /// that are sorted by the keys and are accessible by key
\r
20 public class SortedList : IDictionary, ICollection,
\r
21 IEnumerable, ICloneable {
\r
24 internal struct Slot {
\r
25 internal Object key;
\r
26 internal Object value;
\r
29 private readonly static int INITIAL_SIZE = 16;
\r
31 public enum EnumeratorMode : int {KEY_MODE = 0, VALUE_MODE}
\r
34 private int modificationCount;
\r
35 private Slot[] table;
\r
36 private IComparer comparer;
\r
41 public SortedList () : this (INITIAL_SIZE)
\r
45 public SortedList (int initialCapacity)
\r
46 : this (null, initialCapacity)
\r
50 public SortedList (IComparer comparer, int initialCapacity)
\r
52 if (initialCapacity < 0)
\r
53 throw new ArgumentOutOfRangeException();
\r
55 this.comparer = comparer;
\r
56 InitTable (initialCapacity, true);
\r
59 public SortedList (IComparer comparer)
\r
61 this.comparer = comparer;
\r
62 InitTable (INITIAL_SIZE, true);
\r
66 public SortedList (IDictionary d) : this (d, null)
\r
70 public SortedList (IDictionary d, IComparer comparer)
\r
73 throw new ArgumentNullException ("dictionary");
\r
75 InitTable (d.Count, true);
\r
76 this.comparer = comparer;
\r
78 IDictionaryEnumerator it = d.GetEnumerator ();
\r
79 while (it.MoveNext ()) {
\r
80 if (it.Key is IComparable) {
\r
81 Add (it.Key, it.Value);
\r
83 throw new InvalidCastException("!IComparable");
\r
94 public virtual int Count {
\r
100 public virtual bool IsSynchronized {
\r
106 public virtual Object SyncRoot {
\r
115 public virtual bool IsFixedSize {
\r
122 public virtual bool IsReadOnly {
\r
128 public virtual ICollection Keys {
\r
130 return new ListKeys (this);
\r
134 public virtual ICollection Values {
\r
136 return new ListValues (this);
\r
142 public virtual Object this [Object key] {
\r
145 throw new ArgumentNullException();
\r
146 return GetImpl (key);
\r
150 throw new ArgumentNullException();
\r
152 throw new NotSupportedException("SortedList is Read Only.");
\r
153 if (Find(key) < 0 && IsFixedSize)
\r
154 throw new NotSupportedException("Key not found and SortedList is fixed size.");
\r
156 PutImpl (key, value, true);
\r
160 public virtual int Capacity {
\r
162 return table.Length;
\r
165 Slot [] table = this.table;
\r
166 int current = table.Length;
\r
169 throw new ArgumentOutOfRangeException("capacity too small");
\r
171 if (value > current) {
\r
172 Slot [] newTable = new Slot [value];
\r
173 Array.Copy (table, newTable, current);
\r
174 this.table = newTable;
\r
180 // 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 [INITIAL_SIZE];
\r
203 modificationCount++;
\r
206 public virtual bool Contains (object key)
\r
209 throw new ArgumentNullException();
\r
212 return (Find (key) >= 0);
\r
213 } catch (Exception) {
\r
214 throw new InvalidOperationException();
\r
219 public virtual IDictionaryEnumerator GetEnumerator ()
\r
221 return new Enumerator (this, EnumeratorMode.KEY_MODE);
\r
224 public virtual void Remove (object key)
\r
226 int i = IndexOfKey (key);
\r
227 if (i >= 0) RemoveAt (i);
\r
233 public virtual void CopyTo (Array array, int arrayIndex)
\r
236 throw new ArgumentNullException();
\r
238 if (arrayIndex < 0)
\r
239 throw new ArgumentOutOfRangeException();
\r
241 if (array.Rank > 1)
\r
242 throw new ArgumentException("array is multi-dimensional");
\r
243 if (arrayIndex >= array.Length)
\r
244 throw new ArgumentNullException("arrayIndex is greater than or equal to array.Length");
\r
245 if (Count > (array.Length - arrayIndex))
\r
246 throw new ArgumentNullException("Not enough space in array from arrayIndex to end of array");
\r
248 IDictionaryEnumerator it = GetEnumerator ();
\r
249 int i = arrayIndex;
\r
251 while (it.MoveNext ()) {
\r
252 array.SetValue (it.Entry, i++);
\r
260 public virtual object Clone ()
\r
262 SortedList sl = new SortedList (this, comparer);
\r
263 sl.modificationCount = this.modificationCount;
\r
274 public virtual IList GetKeyList ()
\r
276 return new ListKeys (this);
\r
280 public virtual IList GetValueList ()
\r
282 return new ListValues (this);
\r
286 public virtual void RemoveAt (int index)
\r
288 Slot [] table = this.table;
\r
290 if (index >= 0 && index < cnt) {
\r
291 if (index != cnt - 1) {
\r
292 Array.Copy (table, index+1, table, index, cnt-1-index);
\r
294 table [index].key = null;
\r
295 table [index].value = null;
\r
298 ++modificationCount;
\r
300 throw new ArgumentOutOfRangeException("index out of range");
\r
304 public virtual int IndexOfKey (object key)
\r
307 throw new ArgumentNullException();
\r
312 } catch (Exception) {
\r
313 throw new InvalidOperationException();
\r
316 return (indx | (indx >> 31));
\r
320 public virtual int IndexOfValue (object value)
\r
325 Slot [] table = this.table;
\r
326 int len = table.Length;
\r
328 for (int i=0; i < len; i++) {
\r
329 object trial_value = table[i].value;
\r
330 if ((null != trial_value) && (trial_value.Equals (value))) {
\r
339 public virtual bool ContainsKey (object key)
\r
342 throw new ArgumentNullException();
\r
345 return Contains (key);
\r
346 } catch (Exception) {
\r
347 throw new InvalidOperationException();
\r
352 public virtual bool ContainsValue (object value)
\r
354 return IndexOfValue (value) >= 0;
\r
358 public virtual object GetByIndex (int index)
\r
360 if (index >= 0 && index < Count) {
\r
361 return table [index].value;
\r
363 throw new ArgumentOutOfRangeException("index out of range");
\r
368 public virtual void SetByIndex (int index, object value)
\r
370 if (index >= 0 && index < Count) {
\r
371 table [index].value = value;
\r
373 throw new ArgumentOutOfRangeException("index out of range");
\r
378 public virtual object GetKey (int index)
\r
380 if (index >= 0 && index < Count) {
\r
381 return table [index].key;
\r
383 throw new ArgumentOutOfRangeException("index out of range");
\r
388 public static SortedList Synchronized (SortedList list)
\r
393 public virtual void TrimToSize ()
\r
396 // Trimming an empty SortedList sets the capacity
\r
397 // of the SortedList to the default capacity,
\r
399 if (Count == 0) Resize (INITIAL_SIZE, false);
\r
400 else Resize (Count, true);
\r
409 private void Resize (int n, bool copy)
\r
411 Slot [] table = this.table;
\r
412 Slot [] newTable = new Slot [n];
\r
413 if (copy) Array.Copy (table, 0, newTable, 0, n);
\r
414 this.table = newTable;
\r
418 private void EnsureCapacity (int n, int free)
\r
420 Slot [] table = this.table;
\r
421 Slot [] newTable = null;
\r
422 int cap = Capacity;
\r
423 bool gap = (free >=0 && free < Count);
\r
426 newTable = new Slot [n << 1];
\r
429 if (newTable != null) {
\r
431 int copyLen = free;
\r
433 Array.Copy (table, 0, newTable, 0, copyLen);
\r
435 copyLen = Count - free;
\r
437 Array.Copy (table, free, newTable, free+1, copyLen);
\r
440 // Just a resizing, copy the entire table.
\r
441 Array.Copy (table, newTable, Count);
\r
443 this.table = newTable;
\r
445 Array.Copy (table, free, table, free+1, Count - free);
\r
450 private void PutImpl (object key, object value, bool overwrite)
\r
453 throw new ArgumentNullException ("null key");
\r
455 Slot [] table = this.table;
\r
460 freeIndx = Find (key);
\r
461 } catch (Exception) {
\r
462 throw new InvalidOperationException();
\r
465 if (freeIndx >= 0) {
\r
467 throw new ArgumentException("element already exists");
\r
469 table [freeIndx].value = value;
\r
473 freeIndx = ~freeIndx;
\r
475 if (freeIndx > Capacity + 1)
\r
476 throw new Exception ("SortedList::internal error ("+key+", "+value+") at ["+freeIndx+"]");
\r
479 EnsureCapacity (Count+1, freeIndx);
\r
481 table = this.table;
\r
482 table [freeIndx].key = key;
\r
483 table [freeIndx].value = value;
\r
486 ++modificationCount;
\r
491 private object GetImpl (object key)
\r
493 int i = Find (key);
\r
496 return table [i].value;
\r
501 private void InitTable (int capacity)
\r
503 InitTable (capacity, false);
\r
506 private void InitTable (int capacity, bool force_size) {
\r
507 if (!force_size && (capacity < INITIAL_SIZE)) capacity = INITIAL_SIZE;
\r
508 this.table = new Slot [capacity];
\r
510 this.modificationCount = 0;
\r
513 private void CopyToArray (Array arr, int i,
\r
514 EnumeratorMode mode)
\r
516 IEnumerator it = new Enumerator (this, mode);
\r
518 while (it.MoveNext ()) {
\r
519 arr.SetValue (it.Current, i++);
\r
524 private int Find (object key)
\r
526 Slot [] table = this.table;
\r
529 if (len == 0) return ~0;
\r
531 IComparer comparer = (this.comparer == null)
\r
538 while (left <= right) {
\r
539 int guess = (left + right) >> 1;
\r
541 int cmp = comparer.Compare (key, table[guess].key);
\r
542 if (cmp == 0) return guess;
\r
544 if (cmp > 0) left = guess+1;
\r
545 else right = guess-1;
\r
558 protected sealed class Enumerator : IDictionaryEnumerator,
\r
561 private SortedList host;
\r
565 private EnumeratorMode mode;
\r
567 private object currentKey;
\r
568 private object currentValue;
\r
570 private readonly static string xstr = "SortedList.Enumerator: snapshot out of sync.";
\r
572 public Enumerator (SortedList host, EnumeratorMode mode)
\r
575 stamp = host.modificationCount;
\r
581 public Enumerator (SortedList host)
\r
582 : this (host, EnumeratorMode.KEY_MODE)
\r
587 private void FailFast ()
\r
589 if (host.modificationCount != stamp) {
\r
590 throw new InvalidOperationException (xstr);
\r
594 public void Reset ()
\r
600 currentValue = null;
\r
603 public bool MoveNext ()
\r
607 Slot [] table = host.table;
\r
609 if (++pos < size) {
\r
610 Slot entry = table [pos];
\r
612 currentKey = entry.key;
\r
613 currentValue = entry.value;
\r
618 currentValue = null;
\r
622 public DictionaryEntry Entry
\r
626 return new DictionaryEntry (currentKey,
\r
631 public Object Key {
\r
638 public Object Value {
\r
641 return currentValue;
\r
645 public Object Current {
\r
648 return (mode == EnumeratorMode.KEY_MODE)
\r
656 protected class ListKeys : IList, IEnumerable {
\r
658 private SortedList host;
\r
661 public ListKeys (SortedList host)
\r
664 throw new ArgumentNullException ();
\r
673 public virtual int Count {
\r
679 public virtual bool IsSynchronized {
\r
681 return host.IsSynchronized;
\r
685 public virtual Object SyncRoot {
\r
687 return host.SyncRoot;
\r
691 public virtual void CopyTo (Array array, int arrayIndex)
\r
693 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
\r
701 public virtual bool IsFixedSize {
\r
707 public virtual bool IsReadOnly {
\r
714 public virtual object this [int index] {
\r
716 return host.GetKey (index);
\r
719 throw new NotSupportedException("attempt to modify a key");
\r
723 public virtual int Add (object value)
\r
725 throw new NotSupportedException("IList::Add not supported");
\r
728 public virtual void Clear ()
\r
730 throw new NotSupportedException("IList::Clear not supported");
\r
733 public virtual bool Contains (object key)
\r
735 return host.Contains (key);
\r
739 public virtual int IndexOf (object key)
\r
741 return host.IndexOfKey (key);
\r
745 public virtual void Insert (int index, object value)
\r
747 throw new NotSupportedException("IList::Insert not supported");
\r
751 public virtual void Remove (object value)
\r
753 throw new NotSupportedException("IList::Remove not supported");
\r
757 public virtual void RemoveAt (int index)
\r
759 throw new NotSupportedException("IList::RemoveAt not supported");
\r
767 public virtual IEnumerator GetEnumerator ()
\r
769 return new SortedList.Enumerator (host, EnumeratorMode.KEY_MODE);
\r
776 protected class ListValues : IList, IEnumerable {
\r
778 private SortedList host;
\r
781 public ListValues (SortedList host)
\r
784 throw new ArgumentNullException ();
\r
793 public virtual int Count {
\r
799 public virtual bool IsSynchronized {
\r
801 return host.IsSynchronized;
\r
805 public virtual Object SyncRoot {
\r
807 return host.SyncRoot;
\r
811 public virtual void CopyTo (Array array, int arrayIndex)
\r
813 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
\r
821 public virtual bool IsFixedSize {
\r
827 public virtual bool IsReadOnly {
\r
835 public virtual object this [int index] {
\r
837 return host.GetByIndex (index);
\r
840 // FIXME: It seems (according to tests)
\r
841 // that modifications are allowed
\r
843 // ? host.SetByIndex (index, value);
\r
844 throw new NotSupportedException("attempt to modify a value");
\r
848 public virtual int Add (object value)
\r
850 throw new NotSupportedException("IList::Add not supported");
\r
853 public virtual void Clear ()
\r
855 throw new NotSupportedException("IList::Clear not supported");
\r
858 public virtual bool Contains (object value)
\r
860 return host.ContainsValue (value);
\r
864 public virtual int IndexOf (object value)
\r
866 return host.IndexOfValue (value);
\r
870 public virtual void Insert (int index, object value)
\r
872 throw new NotSupportedException("IList::Insert not supported");
\r
876 public virtual void Remove (object value)
\r
878 throw new NotSupportedException("IList::Remove not supported");
\r
882 public virtual void RemoveAt (int index)
\r
884 throw new NotSupportedException("IList::RemoveAt not supported");
\r
892 public virtual IEnumerator GetEnumerator ()
\r
894 return new SortedList.Enumerator (host, EnumeratorMode.VALUE_MODE);
\r
902 } // System.Collections
\r