259 lines
No EOL
7.1 KiB
C#
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
|
|
}
|
|
} |