using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace ln.collections { public class WeakKeyDictionary : IDictionary where K : class { BTreeValueList> store = new BTreeValueList>(); public WeakKeyDictionary() { } public V this[K key] { get { if (TryGetValue(key, out V value)) return value; throw new KeyNotFoundException(); } set { SetValue(key, value, true); } } public virtual bool KeyEquals(K key1,K key2) { return object.Equals(key1, key2); } public virtual int GetKeyHashcode(K key) => key.GetHashCode(); private WeakKeyValuePair FindKeyValuePair(K key) { int keyHashCode = GetKeyHashcode(key); if (store.ContainsKey(keyHashCode)) foreach (WeakKeyValuePair weakKeyValuePair in store[keyHashCode].ToArray()) { K weakKey = weakKeyValuePair.Key; if (weakKey == null) { store.Remove(keyHashCode, weakKeyValuePair); } else if (KeyEquals(key, weakKey)) { return weakKeyValuePair; } } return null; } public bool TryGetValue(K key, out V value) { WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); if (weakKeyValuePair != null) { value = weakKeyValuePair.Value; return true; } value = default(V); return false; } public K GetKeyInstance(K key) { WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); return weakKeyValuePair.Key; } public void SetValue(K key, V value) => SetValue(key, value, false); public bool SetValue(K key, V value, bool replace) { WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); if (weakKeyValuePair == null) { int keyHashCode = GetKeyHashcode(key); weakKeyValuePair = new WeakKeyValuePair(keyHashCode, key, value); store.Add(keyHashCode, weakKeyValuePair); return false; } else if (replace) { weakKeyValuePair.Value = value; return true; } else { throw new ArgumentException("Key already present"); } } public ICollection Keys { get { List keys = new List(); foreach (WeakKeyValuePair weakKeyValuePair in store.Values.ToArray()) { if (weakKeyValuePair.IsStrong) keys.Add(weakKeyValuePair.Key); else store.TryRemove(weakKeyValuePair.keyHashCode, weakKeyValuePair); } return keys; } } public ICollection Values { get { List values = new List(); foreach (WeakKeyValuePair weakKeyValuePair in store.Values) { if (weakKeyValuePair.IsStrong) values.Add(weakKeyValuePair.Value); else store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair); } return values; } } public int Count => store.Keys.Count(); public bool IsReadOnly => false; public void Add(K key, V value) { SetValue(key, value); } public void Add(KeyValuePair item) => Add(item.Key, item.Value); public void Clear() => store.Clear(); public bool Contains(KeyValuePair item) { WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(item.Key); if ((weakKeyValuePair != null) && weakKeyValuePair.IsStrong) { return Object.Equals(item.Value, weakKeyValuePair.Value); } return false; } public bool ContainsKey(K key) { WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); return (weakKeyValuePair != null) && weakKeyValuePair.IsStrong; } public void CopyTo(KeyValuePair[] array, int arrayIndex) { foreach (WeakKeyValuePair weakKeyValuePair in store.Values) { if (weakKeyValuePair.IsStrong) array[arrayIndex++] = new KeyValuePair(weakKeyValuePair.Key,weakKeyValuePair.Value); } } public IEnumerator> GetEnumerator() { foreach (WeakKeyValuePair weakKeyValuePair in store.Values) { if (weakKeyValuePair.IsStrong) yield return new KeyValuePair(weakKeyValuePair.Key,weakKeyValuePair.Value); } } public bool Remove(K key) { WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); if (weakKeyValuePair != null) { store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair); return true; } return false; } public bool Remove(KeyValuePair item) { WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(item.Key); if ((weakKeyValuePair != null) && (object.Equals(item.Value, weakKeyValuePair.Value))) { store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair); return true; } return false; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); class WeakKeyValuePair where TK: class { public readonly int keyHashCode; WeakReference reference; public TK Key => reference.TryGetTarget(out TK target) ? target : null; public bool IsStrong => reference.TryGetTarget(out TK target); public V Value { get; set; } public WeakKeyValuePair(int keyHashCode,TK key,V value) { reference = new WeakReference(key); this.keyHashCode = keyHashCode; Value = value; } public override int GetHashCode() => keyHashCode; public override bool Equals(object obj) { if (ReferenceEquals(this, obj)) return true; TK key = Key; if ((key != null) && (obj is WeakKeyValuePair)) { WeakKeyValuePair other = obj as WeakKeyValuePair; return Equals(key, other.Key); } return false; } } } }