/Utilities/Collections/SortedList.cs
# · C# · 287 lines · 185 code · 25 blank · 77 comment · 15 complexity · 507e7233273ac4810da09f5873db290b MD5 · raw file
- using System;
- using System.Collections.Generic;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Collections
- {
- /// <summary>
- /// Replacement for System.Collections.Generic.SortedList<K, V>
- /// <para />
- /// SortedList is basically just a List, which sorts the values just before
- /// retrieval. (Only if it is currently unsorted)
- /// Duplicates will be overwritten.
- /// <remarks>
- /// Uses own implementation of quick sort, which is needed to keep key and
- /// value Lists synchronous during sort.
- /// </remarks>
- /// </summary>
- public class SortedList<TKey, TValue>
- where TKey : IComparable<TKey>
- where TValue : IComparable<TValue>
- {
- #region Keys (Public)
- /// <summary>
- /// Keys of the list.
- /// </summary>
- /// <typeparam name="TKey">TKey</typeparam>
- /// <typeparam name="TValue">TValue</typeparam>
- public List<TKey> Keys
- {
- get
- {
- if (isSorted == false)
- {
- Sort();
- }
- return keys;
- } // get
- }
- #endregion
-
- #region Values (Public)
- /// <summary>
- /// Values of the list.
- /// </summary>
- /// <typeparam name="TKey">TKey</typeparam>
- /// <typeparam name="TValue">TValue</typeparam>
- public List<TValue> Values
- {
- get
- {
- if (isSorted == false)
- {
- Sort();
- }
- return values;
- } // get
- }
- #endregion
-
- #region Item (Public)
- /// <summary>
- /// Get or Set the value at index.
- /// </summary>
- /// <param name="index">Index of the value to get/set.</param>
- /// <returns>TValue</returns>
- public TValue this[int index]
- {
- get
- {
- return values[index];
- } // get
- set
- {
- values[index] = value;
- }
- }
- #endregion
-
- #region Count (Public)
- /// <summary>
- /// Returns element count in this collection.
- /// </summary>
- /// <typeparam name="TKey">TKey</typeparam>
- /// <typeparam name="TValue">TValue</typeparam>
- public int Count
- {
- get
- {
- return keys.Count;
- }
- }
- #endregion
-
- #region Private
-
- #region keys (Private)
- /// <summary>
- /// Keys of the list.
- /// </summary>
- /// <typeparam name="TKey">TKey</typeparam>
- /// <typeparam name="TValue">TValue</typeparam>
- private readonly List<TKey> keys;
- #endregion
-
- #region values (Private)
- /// <summary>
- /// Values of the list.
- /// </summary>
- /// <typeparam name="TKey">TKey</typeparam>
- /// <typeparam name="TValue">TValue</typeparam>
- private readonly List<TValue> values;
- #endregion
-
- #region isSorted (Private)
- /// <summary>
- /// If this list currently is in sorted state or not.
- /// </summary>
- /// <typeparam name="TKey">TKey</typeparam>
- /// <typeparam name="TValue">TValue</typeparam>
- private bool isSorted;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create Sorted List
- /// </summary>
- public SortedList()
- {
- keys = new List<TKey>();
- values = new List<TValue>();
- isSorted = false;
- }
- #endregion
-
- #region Add (Public)
- /// <summary>
- /// Adds given Key/Value pair.
- /// If duplicate key is found, value will be replaced.
- /// </summary>
- /// <param name="key">Key</param>
- /// <param name="value">Value</param>
- public void Add(TKey key, TValue value)
- {
- // Note: since this list may be sorted, we could speed up this lookup
- int keyIndex = keys.IndexOf(key);
- if (keyIndex >= 0)
- {
- // Duplicate, replace value only
- values[keyIndex] = value;
- }
- else
- {
- isSorted = false;
- keys.Add(key);
- values.Add(value);
- }
- }
- #endregion
-
- #region Clear (Public)
- /// <summary>
- /// Clears this List.
- /// </summary>
- public void Clear()
- {
- isSorted = false;
- keys.Clear();
- values.Clear();
- }
- #endregion
-
- #region Methods (Private)
-
- #region Sort
- /// <summary>
- /// Sorts keys list, and keeps values list synchronous
- /// </summary>
- private void Sort()
- {
- if (keys.Count > 1)
- {
- Quicksort(0, keys.Count - 1);
- }
- isSorted = true;
- }
- #endregion
-
- #region Quicksort
- /// <summary>
- /// Quicksort implementation, which sorts keys, and rearranges values
- /// accordingly
- /// </summary>
- /// <param name="left">Start Index</param>
- /// <param name="right">End Index</param>
- private void Quicksort(int left, int right)
- {
- int pivotIndex = left;
- int l_hold = left;
- int r_hold = right;
- TKey pivotValue = keys[left];
- TValue pivotValueValue = values[left];
-
- while (left < right)
- {
- while (keys[right].CompareTo(pivotValue) >= 0 &&
- left < right)
- {
- right--;
- }
-
- if (left != right)
- {
- keys[left] = keys[right];
- values[left] = values[right];
- left++;
- }
-
- while (keys[left].CompareTo(pivotValue) <= 0 &&
- left < right)
- {
- left++;
- }
-
- if (left != right)
- {
- keys[right] = keys[left];
- values[right] = values[left];
- right--;
- }
- } // while
-
- keys[left] = pivotValue;
- values[left] = pivotValueValue;
- pivotIndex = left;
- left = l_hold;
- right = r_hold;
-
- if (left < pivotIndex)
- {
- Quicksort(left, pivotIndex - 1);
- }
-
- if (right > pivotIndex)
- {
- Quicksort(pivotIndex + 1, right);
- }
- }
- #endregion
-
- #endregion
- }
-
- /// <summary>
- /// SortedList tests, must be outside of the generic SortedList class to be
- /// able to test it with TestDriven.net. Also must be named uniquely for
- /// </summary>
- internal class SortedListTests
- {
- #region TestAddingEntries (Static)
- /// <summary>
- /// Test adding entries
- /// </summary>
- [Test]
- public static void TestAddingEntries()
- {
- SortedList<float, float> sortedList = new SortedList<float, float>();
- sortedList.Clear();
- sortedList.Add(5, 1);
- sortedList.Add(7, 2);
- sortedList.Add(9, 3);
- sortedList.Add(4, 4);
- // Resulting enumeration should be sorted!
- Assert.Equal(sortedList.Keys[0], 4);
- Assert.Equal(sortedList.Keys[1], 5);
- Assert.Equal(sortedList.Keys[2], 7);
- Assert.Equal(sortedList.Keys[3], 9);
-
- Assert.Equal(sortedList.Values[0], 4);
- Assert.Equal(sortedList.Values[1], 1);
- Assert.Equal(sortedList.Values[2], 2);
- Assert.Equal(sortedList.Values[3], 3);
- }
- #endregion
- }
- }