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

259 lines
No EOL
7.1 KiB
C#

using System;
using System.Collections.Generic;
using JD.Collections.Details;
using JD.Collections.Extensions;
using JD.Collections.Interfaces;
using JD.Collections.ObjectModel;
using JD.Kernel.StaticHelpers;
namespace JD.Collections
{
/// <summary>
/// A KeyedCollection that relies on a HashSet and has no inner list.
/// </summary>
[Serializable]
public abstract class KeyedCollection<TKey, TValue> : CollectionBase<TValue>, IKeyedCollection<TKey, TValue>
{
#region Variables
private IVariableSizeHash<TValue> m_values;
#endregion
#region Constructor
protected KeyedCollection()
{
m_values = new ShortHash<TValue>(new EqualityComparer(this, null));
}
protected KeyedCollection(IEnumerable<TValue> collection)
{
m_values = new ShortHash<TValue>(new EqualityComparer(this, null));
if (collection != null)
{
AddRange(collection);
}
}
protected KeyedCollection(IEqualityComparer<TKey> comparer)
{
m_values = new ShortHash<TValue>(new EqualityComparer(this, comparer));
}
#endregion
#region Methods
/// <summary>
/// Gets the key associtated with the item.
/// </summary>
protected abstract TKey GetKeyForItem(TValue value);
/// <summary>
/// Returns the value if found in the collection.
/// </summary>
public bool TryGetValue(TKey key, out TValue result)
{
return m_values.TryGetValue(new KeyLookup(key, (EqualityComparer)m_values.Comparer), out result);
}
public TValue this[TKey key]
{
get
{
TValue result;
Throw.InvalidArgumentIf(!TryGetValue(key, out result), "Key is not present in dictionary", "key");
return result;
}
}
public override bool Contains(TValue item)
{
return m_values.Contains(item);
}
/// <summary>
/// Same as ContainsKey, just there to be consistent with .net KeyedCollection
/// </summary>
public bool Contains(TKey key)
{
return ContainsKey(key);
}
public bool ContainsKey(TKey key)
{
TValue dummy;
return TryGetValue(key, out dummy);
}
public bool Remove(TKey key)
{
TValue ignored;
return Remove(key, out ignored);
}
public bool Remove(TKey key, out TValue removedValue)
{
return m_values.Remove(new KeyLookup(key, (EqualityComparer)m_values.Comparer), out removedValue);
}
public void AddRange(IEnumerable<TValue> enumerable)
{
if (enumerable is ICollection<TValue> collection)
{
m_values.Capacity = collection.Count + m_values.Count;
}
enumerable.ForEach(Add);
}
public void TrimExcess()
{
m_values.TrimExcess();
}
public bool Replace(TValue item)
{
return m_values.Add(item, CollisionBehavior.Replace, ref m_values);
}
public bool TryAdd(TValue item)
{
return m_values.Add(item, CollisionBehavior.Replace, ref m_values);
}
public override void Add(TValue item)
{
m_values.Add(item, CollisionBehavior.Throw, ref m_values);
}
public override void Clear()
{
m_values.Clear();
}
public override bool Remove(TValue item)
{
return m_values.Remove(item);
}
public override int Count
{
get
{
return m_values.Count;
}
}
public override IEnumerator<TValue> GetEnumerator()
{
return m_values.GetEnumerator();
}
/// <summary>
/// Creates a concrete collection from a converter.
/// </summary>
/// <param name="getKeyForItem"></param>
/// <returns></returns>
public static KeyedCollection<TKey, TValue> Create(Converter<TValue, TKey> getKeyForItem)
{
return new ConcreteKeyedCollection(getKeyForItem);
}
/// <summary>
/// Enumerates the content of the hash but does not garantee to return elements added after the enumeration started. Garantees to not throw or not cause any threading issue.
/// </summary>
public IEnumerable<TValue> ThreadSafeEnumerate
{
get
{
return m_values.ThreadSafeEnumerate;
}
}
#endregion
#region Nested
[Serializable]
private class EqualityComparer : IEqualityComparer<TValue>
{
private readonly KeyedCollection<TKey, TValue> m_owner;
private readonly IEqualityComparer<TKey> m_keyComparer;
public EqualityComparer(KeyedCollection<TKey, TValue> owner, IEqualityComparer<TKey> keyComparer)
{
m_owner = owner;
m_keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
}
public bool Equals(TValue x, TValue y)
{
return KeyEquals(m_owner.GetKeyForItem(x), m_owner.GetKeyForItem(y));
}
public int GetHashCode(TValue obj)
{
return GetHashCode(m_owner.GetKeyForItem(obj));
}
public int GetHashCode(TKey key)
{
return Equals(key, null) ? 0 : m_keyComparer.GetHashCode(key);
}
public bool KeyEquals(TKey a, TKey b)
{
return m_keyComparer.Equals(a, b);
}
public TKey GetKeyForItem(TValue value)
{
return m_owner.GetKeyForItem(value);
}
}
private class KeyLookup : ILookup<TValue>
{
private readonly TKey m_key;
private readonly EqualityComparer m_comparer;
public KeyLookup(TKey key, EqualityComparer comparer)
{
m_key = key;
m_comparer = comparer;
}
public bool Equals(TValue other)
{
return m_comparer.KeyEquals(m_comparer.GetKeyForItem(other), m_key);
}
public int HashCode
{
get { return m_comparer.GetHashCode(m_key); }
}
}
private class ConcreteKeyedCollection : KeyedCollection<TKey, TValue>
{
private readonly Converter<TValue, TKey> m_getKeyForItem;
public ConcreteKeyedCollection(Converter<TValue, TKey> getKeyForItem)
{
Throw.IfNull(getKeyForItem, "getKeyForItem");
m_getKeyForItem = getKeyForItem;
}
protected override TKey GetKeyForItem(TValue item)
{
return m_getKeyForItem(item);
}
}
#endregion
}
}