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

184 lines
No EOL
4.6 KiB
C#

using System.Collections;
using System.Collections.Generic;
using JD.Collections.Details;
using JD.Kernel.StaticHelpers;
namespace JD.Collections
{
/// <summary>
/// A collection that allows multiple item to share the same key. Operations are in the order of clash count.
/// </summary>
public abstract class MultiKeyedCollection<TKey, TValue> : IEnumerable<TValue>
{
#region Variables
private readonly IEqualityComparer<TKey> m_keyEqualityComparer;
private readonly FastHash<TValue> m_hash;
#endregion
#region Constructor
protected MultiKeyedCollection()
: this(EqualityComparer<TKey>.Default)
{
}
protected MultiKeyedCollection(IEqualityComparer<TKey> keyComparer)
{
Throw.IfNull(keyComparer, "keyComparer");
m_keyEqualityComparer = keyComparer;
m_hash = new FastHash<TValue>(new NeverEqualComparer(this));
}
#endregion
#region Properties
public int Count
{
get { return m_hash.Count; }
}
#endregion
#region Methods
public void Add(TValue value)
{
m_hash.Add(value);
}
public bool Remove(TValue value)
{
TValue dummy;
return Remove(value, out dummy);
}
public bool Remove(TValue value, out TValue removed)
{
return m_hash.Remove(new ValueLookup(value, this), out removed);
}
public IEnumerable<TValue> this[TKey key]
{
get { return m_hash.EnumerateValues(new KeyLookup(key, this)); }
}
private int GetHashCodeFor(TValue value)
{
return GetHashCode(GetKeyForItem(value));
}
protected abstract TKey GetKeyForItem(TValue value);
private bool KeyEquals(TKey a, TKey b)
{
return m_keyEqualityComparer.Equals(a, b);
}
private int GetHashCode(TKey key)
{
return m_keyEqualityComparer.GetHashCode(key);
}
protected virtual bool ValueEquals(TValue a, TValue b)
{
return Equals(a, b);
}
#endregion
#region IEnumerable
public IEnumerator<TValue> GetEnumerator()
{
return m_hash.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Nested
/// <summary>
/// When adding a value, we consider each value is not equal.
/// </summary>
private class NeverEqualComparer : IEqualityComparer<TValue>
{
private readonly MultiKeyedCollection<TKey, TValue> m_owner;
public NeverEqualComparer(MultiKeyedCollection<TKey, TValue> owner)
{
m_owner = owner;
}
public bool Equals(TValue x, TValue y)
{
return false;
}
public int GetHashCode(TValue obj)
{
return m_owner.GetHashCodeFor(obj);
}
}
/// <summary>
/// When deleting a value, we delete the first that is equal to the object.
/// </summary>
private class ValueLookup : ILookup<TValue>
{
private readonly MultiKeyedCollection<TKey, TValue> m_owner;
private readonly TValue m_value;
public ValueLookup(TValue value, MultiKeyedCollection<TKey, TValue> owner)
{
m_value = value;
m_owner = owner;
}
public int HashCode
{
get { return m_owner.GetHashCodeFor(m_value); }
}
public bool Equals(TValue other)
{
return m_owner.ValueEquals(m_value, other);
}
}
/// <summary>
/// When enumerating, enumerate all objects that shares the same key.
/// </summary>
private class KeyLookup : ILookup<TValue>
{
private readonly MultiKeyedCollection<TKey, TValue> m_owner;
private readonly TKey m_key;
public KeyLookup(TKey key, MultiKeyedCollection<TKey, TValue> owner)
{
m_owner = owner;
m_key = key;
}
public int HashCode
{
get { return m_owner.GetHashCode(m_key); }
}
public bool Equals(TValue other)
{
return m_owner.KeyEquals(m_key, m_owner.GetKeyForItem(other));
}
}
#endregion
}
}