316 lines
8.1 KiB
C#
316 lines
8.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using JD.Collections.ObjectModel;
|
|
using JD.Kernel.StaticHelpers;
|
|
|
|
namespace JD.Collections
|
|
{
|
|
/// <summary>
|
|
/// List that create an index after a threshold.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
public class HybridList<T> : ListBase<T>
|
|
{
|
|
#region Constants
|
|
|
|
private const int DefaultThreshold = 10;
|
|
private const string DuplicateErrorMessage = "The Hybrid list doesn't support duplicated item. Item: {0}";
|
|
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
private readonly int m_thresholdMinusOne = DefaultThreshold - 1;
|
|
private readonly List<T> m_innerList = new List<T>();
|
|
private Set<T> m_index;
|
|
|
|
#endregion
|
|
|
|
#region Ctors
|
|
|
|
/// <summary>
|
|
/// Ctor.
|
|
/// </summary>
|
|
public HybridList()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ctor.
|
|
/// </summary>
|
|
/// <param name="threshold">Threshold after which to create an index.</param>
|
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="threshold"/> is less than 1.</exception>
|
|
public HybridList(int threshold)
|
|
{
|
|
Throw.ArgumentOutOfRangeIf(threshold < 1, "Threshold must be at least 1.", "threshold");
|
|
|
|
m_thresholdMinusOne = threshold - 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ctor.
|
|
/// </summary>
|
|
/// <param name="threshold">Threshold after which to create an index.</param>
|
|
/// <param name="capacity">Capacity.</param>
|
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="threshold"/> is less than 1.</exception>
|
|
public HybridList(int threshold, int capacity)
|
|
: this(threshold)
|
|
{
|
|
m_innerList = new List<T>(capacity);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ctor.
|
|
/// </summary>
|
|
/// <param name="collection">The collection whose elements are copied to the new list.</param>
|
|
/// <exception cref="ArgumentException">If the <paramref name="collection"/> contains duplicated items.</exception>
|
|
public HybridList(IEnumerable<T> collection)
|
|
: this(collection, DefaultThreshold)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ctor.
|
|
/// </summary>
|
|
/// <param name="collection">The collection whose elements are copied to the new list.</param>
|
|
/// <param name="threshold">Threshold after which to create an index.</param>
|
|
/// <exception cref="ArgumentException">If the <paramref name="collection"/> contains duplicated items.</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="threshold"/> is less than 1.</exception>
|
|
public HybridList(IEnumerable<T> collection, int threshold)
|
|
: this(threshold)
|
|
{
|
|
m_innerList = new List<T>(collection);
|
|
|
|
if (m_innerList.Count >= threshold)
|
|
{
|
|
CreateIndex();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Count.
|
|
/// </summary>
|
|
public override int Count
|
|
{
|
|
get
|
|
{
|
|
return m_innerList.Count;
|
|
}
|
|
}
|
|
|
|
public bool Indexed
|
|
{
|
|
get { return m_index != null; }
|
|
}
|
|
|
|
public int Threshold
|
|
{
|
|
get { return m_thresholdMinusOne + 1; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
protected override T GetItem(int index)
|
|
{
|
|
return m_innerList[index];
|
|
}
|
|
|
|
protected override void SetItem(int index, T value)
|
|
{
|
|
T oldValue = m_innerList[index];
|
|
|
|
if (!Equals(oldValue, value))
|
|
{
|
|
//Not trying to set same item at the same position. So we can check for duplicate.
|
|
ThrowIfDuplicated(value);
|
|
|
|
Unindex(oldValue);
|
|
m_innerList[index] = value;
|
|
AddIndex(value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a item.
|
|
/// </summary>
|
|
/// <exception cref="NotSupportedException">If <paramref name="item"/> is already contains in the list.</exception>
|
|
public override void Add(T item)
|
|
{
|
|
AddAndIndex(item, m_innerList.Add);
|
|
}
|
|
|
|
public override void Clear()
|
|
{
|
|
m_innerList.Clear();
|
|
m_index = null;
|
|
}
|
|
|
|
public override bool Remove(T item)
|
|
{
|
|
if(Indexed)
|
|
{
|
|
if(m_index.Remove(item))
|
|
{
|
|
bool removedFromList = m_innerList.Remove(item);
|
|
System.Diagnostics.Debug.Assert(removedFromList);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return m_innerList.Remove(item);
|
|
}
|
|
|
|
public override bool Contains(T item)
|
|
{
|
|
if (Indexed)
|
|
{
|
|
return m_index.Contains(item);
|
|
}
|
|
|
|
return m_innerList.Contains(item);
|
|
}
|
|
|
|
public override int IndexOf(T item)
|
|
{
|
|
return m_innerList.IndexOf(item);
|
|
}
|
|
|
|
public override void Insert(int index, T item)
|
|
{
|
|
AddAndIndex(item, delegate(T _item)
|
|
{
|
|
m_innerList.Insert(index, _item);
|
|
});
|
|
}
|
|
|
|
public override void RemoveAt(int index)
|
|
{
|
|
Unindex(m_innerList[index]);
|
|
m_innerList.RemoveAt(index);
|
|
}
|
|
|
|
public override void CopyTo(T[] array, int arrayIndex)
|
|
{
|
|
m_innerList.CopyTo(array, arrayIndex);
|
|
}
|
|
|
|
public override T[] ToArray()
|
|
{
|
|
return m_innerList.ToArray();
|
|
}
|
|
|
|
public override IEnumerator<T> GetEnumerator()
|
|
{
|
|
return m_innerList.GetEnumerator();
|
|
}
|
|
|
|
#region Index management
|
|
|
|
private void AddIndex(T item)
|
|
{
|
|
if (CreateIndexIfNeeded())
|
|
{
|
|
m_index.Add(item);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Index a new value. The list must be indexed.
|
|
/// </summary>
|
|
/// <returns>true if the item have been added. Not a duplicate.</returns>
|
|
private bool UnsafeAddIndex(T item)
|
|
{
|
|
System.Diagnostics.Debug.Assert(Indexed);
|
|
|
|
return m_index.Add(item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create the index if needed.
|
|
/// </summary>
|
|
/// <returns>True if the list is indexed.</returns>
|
|
private bool CreateIndexIfNeeded()
|
|
{
|
|
if(Indexed)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (m_innerList.Count >= m_thresholdMinusOne)
|
|
{
|
|
CreateIndex();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void CreateIndex()
|
|
{
|
|
m_index = new Set<T>(m_innerList);
|
|
|
|
if (m_index.Count != m_innerList.Count)
|
|
{
|
|
throw new InvalidOperationException("The collection contains duplicated items.");
|
|
}
|
|
}
|
|
|
|
private void Unindex(T item)
|
|
{
|
|
if (Indexed)
|
|
{
|
|
m_index.Remove(item);
|
|
}
|
|
}
|
|
|
|
private void AddAndIndex(T item, Action<T> addOperation)
|
|
{
|
|
if(CreateIndexIfNeeded())
|
|
{
|
|
//Indexed
|
|
if (!UnsafeAddIndex(item))
|
|
{
|
|
ThrowDuplicated(item);
|
|
}
|
|
addOperation(item);
|
|
}
|
|
else
|
|
{
|
|
//Not indexed
|
|
ThrowIfDuplicated(item);
|
|
|
|
addOperation(item);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Exception
|
|
|
|
private void ThrowIfDuplicated(T value)
|
|
{
|
|
if(Contains(value))
|
|
{
|
|
ThrowDuplicated(value);
|
|
}
|
|
}
|
|
|
|
private static void ThrowDuplicated(T value)
|
|
{
|
|
throw new NotSupportedException(string.Format(DuplicateErrorMessage, value));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
}
|