/Utilities/Collections/SortedList.cs
C# | 287 lines | 185 code | 25 blank | 77 comment | 15 complexity | 507e7233273ac4810da09f5873db290b MD5 | raw file
1using System; 2using System.Collections.Generic; 3using NUnit.Framework; 4 5namespace Delta.Utilities.Collections 6{ 7 /// <summary> 8 /// Replacement for System.Collections.Generic.SortedList<K, V> 9 /// <para /> 10 /// SortedList is basically just a List, which sorts the values just before 11 /// retrieval. (Only if it is currently unsorted) 12 /// Duplicates will be overwritten. 13 /// <remarks> 14 /// Uses own implementation of quick sort, which is needed to keep key and 15 /// value Lists synchronous during sort. 16 /// </remarks> 17 /// </summary> 18 public class SortedList<TKey, TValue> 19 where TKey : IComparable<TKey> 20 where TValue : IComparable<TValue> 21 { 22 #region Keys (Public) 23 /// <summary> 24 /// Keys of the list. 25 /// </summary> 26 /// <typeparam name="TKey">TKey</typeparam> 27 /// <typeparam name="TValue">TValue</typeparam> 28 public List<TKey> Keys 29 { 30 get 31 { 32 if (isSorted == false) 33 { 34 Sort(); 35 } 36 return keys; 37 } // get 38 } 39 #endregion 40 41 #region Values (Public) 42 /// <summary> 43 /// Values of the list. 44 /// </summary> 45 /// <typeparam name="TKey">TKey</typeparam> 46 /// <typeparam name="TValue">TValue</typeparam> 47 public List<TValue> Values 48 { 49 get 50 { 51 if (isSorted == false) 52 { 53 Sort(); 54 } 55 return values; 56 } // get 57 } 58 #endregion 59 60 #region Item (Public) 61 /// <summary> 62 /// Get or Set the value at index. 63 /// </summary> 64 /// <param name="index">Index of the value to get/set.</param> 65 /// <returns>TValue</returns> 66 public TValue this[int index] 67 { 68 get 69 { 70 return values[index]; 71 } // get 72 set 73 { 74 values[index] = value; 75 } 76 } 77 #endregion 78 79 #region Count (Public) 80 /// <summary> 81 /// Returns element count in this collection. 82 /// </summary> 83 /// <typeparam name="TKey">TKey</typeparam> 84 /// <typeparam name="TValue">TValue</typeparam> 85 public int Count 86 { 87 get 88 { 89 return keys.Count; 90 } 91 } 92 #endregion 93 94 #region Private 95 96 #region keys (Private) 97 /// <summary> 98 /// Keys of the list. 99 /// </summary> 100 /// <typeparam name="TKey">TKey</typeparam> 101 /// <typeparam name="TValue">TValue</typeparam> 102 private readonly List<TKey> keys; 103 #endregion 104 105 #region values (Private) 106 /// <summary> 107 /// Values of the list. 108 /// </summary> 109 /// <typeparam name="TKey">TKey</typeparam> 110 /// <typeparam name="TValue">TValue</typeparam> 111 private readonly List<TValue> values; 112 #endregion 113 114 #region isSorted (Private) 115 /// <summary> 116 /// If this list currently is in sorted state or not. 117 /// </summary> 118 /// <typeparam name="TKey">TKey</typeparam> 119 /// <typeparam name="TValue">TValue</typeparam> 120 private bool isSorted; 121 #endregion 122 123 #endregion 124 125 #region Constructors 126 /// <summary> 127 /// Create Sorted List 128 /// </summary> 129 public SortedList() 130 { 131 keys = new List<TKey>(); 132 values = new List<TValue>(); 133 isSorted = false; 134 } 135 #endregion 136 137 #region Add (Public) 138 /// <summary> 139 /// Adds given Key/Value pair. 140 /// If duplicate key is found, value will be replaced. 141 /// </summary> 142 /// <param name="key">Key</param> 143 /// <param name="value">Value</param> 144 public void Add(TKey key, TValue value) 145 { 146 // Note: since this list may be sorted, we could speed up this lookup 147 int keyIndex = keys.IndexOf(key); 148 if (keyIndex >= 0) 149 { 150 // Duplicate, replace value only 151 values[keyIndex] = value; 152 } 153 else 154 { 155 isSorted = false; 156 keys.Add(key); 157 values.Add(value); 158 } 159 } 160 #endregion 161 162 #region Clear (Public) 163 /// <summary> 164 /// Clears this List. 165 /// </summary> 166 public void Clear() 167 { 168 isSorted = false; 169 keys.Clear(); 170 values.Clear(); 171 } 172 #endregion 173 174 #region Methods (Private) 175 176 #region Sort 177 /// <summary> 178 /// Sorts keys list, and keeps values list synchronous 179 /// </summary> 180 private void Sort() 181 { 182 if (keys.Count > 1) 183 { 184 Quicksort(0, keys.Count - 1); 185 } 186 isSorted = true; 187 } 188 #endregion 189 190 #region Quicksort 191 /// <summary> 192 /// Quicksort implementation, which sorts keys, and rearranges values 193 /// accordingly 194 /// </summary> 195 /// <param name="left">Start Index</param> 196 /// <param name="right">End Index</param> 197 private void Quicksort(int left, int right) 198 { 199 int pivotIndex = left; 200 int l_hold = left; 201 int r_hold = right; 202 TKey pivotValue = keys[left]; 203 TValue pivotValueValue = values[left]; 204 205 while (left < right) 206 { 207 while (keys[right].CompareTo(pivotValue) >= 0 && 208 left < right) 209 { 210 right--; 211 } 212 213 if (left != right) 214 { 215 keys[left] = keys[right]; 216 values[left] = values[right]; 217 left++; 218 } 219 220 while (keys[left].CompareTo(pivotValue) <= 0 && 221 left < right) 222 { 223 left++; 224 } 225 226 if (left != right) 227 { 228 keys[right] = keys[left]; 229 values[right] = values[left]; 230 right--; 231 } 232 } // while 233 234 keys[left] = pivotValue; 235 values[left] = pivotValueValue; 236 pivotIndex = left; 237 left = l_hold; 238 right = r_hold; 239 240 if (left < pivotIndex) 241 { 242 Quicksort(left, pivotIndex - 1); 243 } 244 245 if (right > pivotIndex) 246 { 247 Quicksort(pivotIndex + 1, right); 248 } 249 } 250 #endregion 251 252 #endregion 253 } 254 255 /// <summary> 256 /// SortedList tests, must be outside of the generic SortedList class to be 257 /// able to test it with TestDriven.net. Also must be named uniquely for 258 /// </summary> 259 internal class SortedListTests 260 { 261 #region TestAddingEntries (Static) 262 /// <summary> 263 /// Test adding entries 264 /// </summary> 265 [Test] 266 public static void TestAddingEntries() 267 { 268 SortedList<float, float> sortedList = new SortedList<float, float>(); 269 sortedList.Clear(); 270 sortedList.Add(5, 1); 271 sortedList.Add(7, 2); 272 sortedList.Add(9, 3); 273 sortedList.Add(4, 4); 274 // Resulting enumeration should be sorted! 275 Assert.Equal(sortedList.Keys[0], 4); 276 Assert.Equal(sortedList.Keys[1], 5); 277 Assert.Equal(sortedList.Keys[2], 7); 278 Assert.Equal(sortedList.Keys[3], 9); 279 280 Assert.Equal(sortedList.Values[0], 4); 281 Assert.Equal(sortedList.Values[1], 1); 282 Assert.Equal(sortedList.Values[2], 2); 283 Assert.Equal(sortedList.Values[3], 3); 284 } 285 #endregion 286 } 287}