JD2022-TU1/main/tools/framework/JD.Collections/HybridList.cs

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
}
}