PageRenderTime 27ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Collections/UniqueList.cs

#
C# | 348 lines | 195 code | 28 blank | 125 comment | 2 complexity | eb8850683e507a72fc6f3136382ce26f MD5 | raw file
Possible License(s): Apache-2.0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using Delta.Utilities.Helpers;
  4. using NUnit.Framework;
  5. namespace Delta.Utilities.Collections
  6. {
  7. /// <summary>
  8. /// Based on simple List&lt;T&gt; generic collection, but this also checks
  9. /// if elements do already exists. It will not add duplicates, duplicates
  10. /// are ignored!
  11. /// </summary>
  12. /// <typeparam name="T">T</typeparam>
  13. public class UniqueList<T>
  14. : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable,
  15. ICloneable<UniqueList<T>>
  16. {
  17. #region Item (Public)
  18. /// <summary>
  19. /// This
  20. /// </summary>
  21. /// <param name="index">Index</param>
  22. /// <returns>T</returns>
  23. public T this[int index]
  24. {
  25. get
  26. {
  27. return data[index];
  28. }
  29. set
  30. {
  31. data[index] = value;
  32. }
  33. }
  34. #endregion
  35. #region Count (Public)
  36. /// <summary>
  37. /// Count, returns number of entries in the list
  38. /// </summary>
  39. /// <typeparam name="T">T</typeparam>
  40. /// <returns>Number of entries in the list</returns>
  41. public int Count
  42. {
  43. get
  44. {
  45. return data.Count;
  46. } // get
  47. }
  48. #endregion
  49. #region IsReadOnly (Public)
  50. /// <summary>
  51. /// Is this list read only? Will always return false.
  52. /// </summary>
  53. /// <typeparam name="T">T</typeparam>
  54. /// <returns>
  55. /// Will always returns false because this list is never read only
  56. /// </returns>
  57. public bool IsReadOnly
  58. {
  59. get
  60. {
  61. return false;
  62. } // get
  63. }
  64. #endregion
  65. #region Private
  66. #region data (Private)
  67. /// <summary>
  68. /// Using List&lt;T&gt;, we can't derive from it to override the methods.
  69. /// </summary>
  70. /// <typeparam name="T">T</typeparam>
  71. private readonly List<T> data = new List<T>();
  72. #endregion
  73. #endregion
  74. #region Constructors
  75. /// <summary>
  76. /// Create unique list
  77. /// </summary>
  78. public UniqueList()
  79. {
  80. }
  81. /// <summary>
  82. /// Create unique list
  83. /// </summary>
  84. /// <param name="copyFrom">Copy data from this enumerable collection,
  85. /// if this collection contains multiple same entries, only the first
  86. /// instances of them will be added (since this is an unique list).
  87. /// </param>
  88. public UniqueList(ICollection<T> copyFrom)
  89. {
  90. AddRange(copyFrom);
  91. }
  92. #endregion
  93. #region ICloneable<UniqueList<T>> Members
  94. /// <summary>
  95. /// Clone
  96. /// </summary>
  97. public UniqueList<T> Clone()
  98. {
  99. return new UniqueList<T>(this);
  100. }
  101. #endregion
  102. #region ICollection<T> Members
  103. /// <summary>
  104. /// Add
  105. /// </summary>
  106. /// <param name="item">Item</param>
  107. public void Add(T item)
  108. {
  109. // Does already exists?
  110. if (data.IndexOf(item) >= 0)
  111. {
  112. // Then don't add again
  113. return;
  114. }
  115. // Else not found, add to list
  116. data.Add(item);
  117. }
  118. /// <summary>
  119. /// Clear
  120. /// </summary>
  121. public void Clear()
  122. {
  123. data.Clear();
  124. }
  125. /// <summary>
  126. /// Contains
  127. /// </summary>
  128. /// <param name="item">Item</param>
  129. /// <returns>True if this list contains the item, false otherwise</returns>
  130. public bool Contains(T item)
  131. {
  132. return data.Contains(item);
  133. }
  134. /// <summary>
  135. /// Copy to
  136. /// </summary>
  137. /// <param name="array">Array</param>
  138. /// <param name="arrayIndex">Array index</param>
  139. public void CopyTo(T[] array, int arrayIndex)
  140. {
  141. data.CopyTo(array, arrayIndex);
  142. }
  143. /// <summary>
  144. /// Remove
  145. /// </summary>
  146. /// <param name="item">Item</param>
  147. /// <returns>True if the item was removed successfully</returns>
  148. public bool Remove(T item)
  149. {
  150. return data.Remove(item);
  151. }
  152. #endregion
  153. #region IEnumerable Members
  154. /// <summary>
  155. /// System. collections. i enumerable. get enumerator
  156. /// </summary>
  157. /// <returns>System. collections. i enumerator</returns>
  158. IEnumerator
  159. IEnumerable.GetEnumerator()
  160. {
  161. return GetEnumerator();
  162. }
  163. #endregion
  164. #region IEnumerable<T> Members
  165. /// <summary>
  166. /// GetEnumerator
  167. /// </summary>
  168. /// <returns>IEnumerator</returns>
  169. public IEnumerator<T> GetEnumerator()
  170. {
  171. return data.GetEnumerator();
  172. }
  173. #endregion
  174. #region IList<T> Members
  175. /// <summary>
  176. /// Index of
  177. /// </summary>
  178. /// <param name="item">Item</param>
  179. /// <returns>Int</returns>
  180. public int IndexOf(T item)
  181. {
  182. return data.IndexOf(item);
  183. }
  184. /// <summary>
  185. /// Insert
  186. /// </summary>
  187. /// <param name="index">Index</param>
  188. /// <param name="item">Item</param>
  189. public void Insert(int index, T item)
  190. {
  191. // Does already exists?
  192. if (data.IndexOf(item) >= 0)
  193. {
  194. // Then don't add again
  195. return;
  196. // Else not found, add to list
  197. }
  198. data.Insert(index, item);
  199. }
  200. /// <summary>
  201. /// Remove at
  202. /// </summary>
  203. /// <param name="index">Index</param>
  204. public void RemoveAt(int index)
  205. {
  206. data.RemoveAt(index);
  207. }
  208. #endregion
  209. #region AddRange (Public)
  210. /// <summary>
  211. /// Add range, will just use Add to add all elements.
  212. /// </summary>
  213. /// <param name="c">Collection to add</param>
  214. public void AddRange(ICollection<T> c)
  215. {
  216. foreach (T obj in c)
  217. {
  218. Add(obj);
  219. }
  220. }
  221. #endregion
  222. #region Sort (Public)
  223. /// <summary>
  224. /// Sort list, same as List&lt;T&gt;
  225. /// </summary>
  226. /// <param name="comparer">Comparer</param>
  227. public void Sort(IComparer<T> comparer)
  228. {
  229. data.Sort(comparer);
  230. }
  231. /// <summary>
  232. /// Sort list, same as List&lt;T&gt;
  233. /// </summary>
  234. public void Sort()
  235. {
  236. data.Sort();
  237. }
  238. #endregion
  239. #region ToArray (Public)
  240. /// <summary>
  241. /// To array helper method, very useful to convert generic dynamic list
  242. /// to simple static list.
  243. /// </summary>
  244. /// <returns>Array</returns>
  245. public T[] ToArray()
  246. {
  247. return data.ToArray();
  248. }
  249. #endregion
  250. #region ToList (Public)
  251. /// <summary>
  252. /// To array helper method, very useful to convert generic dynamic list
  253. /// to simple static list. Internally this is a list anyway ^^
  254. /// </summary>
  255. /// <returns>Array</returns>
  256. public List<T> ToList()
  257. {
  258. return data;
  259. }
  260. #endregion
  261. }
  262. /// <summary>
  263. /// UniqueList tests, needs to be an extra class because
  264. /// nesting in generic classes is not supported by NUnit or xUnit.
  265. /// </summary>
  266. internal class UniqueListTests
  267. {
  268. #region TestUniqueList
  269. /// <summary>
  270. /// Test unique list
  271. /// </summary>
  272. [Test]
  273. public void TestUniqueList()
  274. {
  275. UniqueList<int> list = new UniqueList<int>();
  276. list.Add(2);
  277. list.Add(3);
  278. list.Add(4);
  279. list.Add(2);
  280. Assert.Equal(3, list.Count);
  281. Assert.Equal(2, list[0]);
  282. Assert.Equal("2, 3, 4",
  283. list.Write());
  284. list.Remove(2);
  285. // 5 does not exists, the following remove will be ignored.
  286. list.Remove(5);
  287. Assert.Equal("3, 4",
  288. list.Write());
  289. int[] intList = list.ToArray();
  290. Assert.Equal(3, intList[0]);
  291. Assert.Equal(4, intList[1]);
  292. Assert.Equal(2, intList.Length);
  293. }
  294. #endregion
  295. #region TestConstructor
  296. /// <summary>
  297. /// Test the constructor of UniqueList, which should automatically remove
  298. /// duplicates for us when we pass in an existing list.
  299. /// </summary>
  300. [Test]
  301. public void TestConstructor()
  302. {
  303. List<int> listWithDuplicates = new List<int>();
  304. listWithDuplicates.Add(3);
  305. listWithDuplicates.Add(5);
  306. listWithDuplicates.Add(7);
  307. listWithDuplicates.Add(3);
  308. UniqueList<int> copiedUniqueList =
  309. new UniqueList<int>(listWithDuplicates);
  310. // The original list with duplicates has 4 elements
  311. Assert.Equal(listWithDuplicates.Count, 4);
  312. // The unique list should only contain 3 elements (3, 5, and 7)
  313. Assert.Equal(copiedUniqueList.Count, 3);
  314. Assert.Equal(copiedUniqueList[0], 3);
  315. Assert.Equal(copiedUniqueList[1], 5);
  316. Assert.Equal(copiedUniqueList[2], 7);
  317. }
  318. #endregion
  319. }
  320. }