PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Collections/SortedList.cs

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