PageRenderTime 144ms CodeModel.GetById 60ms app.highlight 10ms RepoModel.GetById 71ms app.codeStats 0ms

/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&lt;K, V&gt;
  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}