// ----------------------------------------------------------------------- // // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // ----------------------------------------------------------------------- namespace Lucene.Net.Support { using System; using System.Collections.Generic; using System.Linq; using System.Text; /// /// A dictionary of weak references. /// /// /// /// /// C# File: /// src/Lucene.Net/Support/WeakDictionaryOfTKeyTValue.cs /// /// /// /// C# Tests: /// test/Lucene.Net.Test/Support/WeakDictionaryOfTKeyTValueTest.cs /// /// /// /// /// Heavily base on the implementation found here on /// Nick Guerrera's Blog /// /// /// This was implemented to be the C# equivalent of has a Java WeakHashMap. /// /// /// The TKey type. /// The TValue type. public sealed class WeakDictionary : BaseDictionary where TKey : class where TValue : class { private Dictionary> internalDictionary; private WeakKeyComparer comparer; private int initialCapacity = 0; private DateTime lastRemoval = DateTime.Now; /// /// Initializes a new instance of the class. /// public WeakDictionary() : this(0, null) { } /// /// Initializes a new instance of the class. /// /// The capacity. public WeakDictionary(int capacity) : this(capacity, null) { } /// /// Initializes a new instance of the class. /// /// The comparer. public WeakDictionary(IEqualityComparer comparer) : this(0, comparer) { } /// /// Initializes a new instance of the class. /// /// The capacity. /// The comparer. public WeakDictionary(int capacity, IEqualityComparer comparer) { this.PeriodicRemoval = new TimeSpan(0, 0, 15); this.initialCapacity = capacity; this.comparer = new WeakKeyComparer(comparer); this.internalDictionary = new Dictionary>(capacity, this.comparer); } /// /// Initializes a new instance of the class. /// /// The dictionary. public WeakDictionary(IDictionary dictionary) : this(0, null) { foreach (var pair in dictionary) this.Add(pair.Key, pair.Value); } /// /// Gets the comparer. /// /// The comparer. public IEqualityComparer Comparer { get { return this.comparer.InternalComparer; } } // WARNING: The count returned here may include entries for which // either the key or value objects have already been garbage // collected. Call RemoveCollectedEntries to weed out collected // entries and update the count accordingly. /// /// Gets the count. /// /// The count. public override int Count { get { this.EnsureRemovalOfCollectedEntries(); return this.internalDictionary.Count; } } /// /// Gets the initial capacity. /// /// The initial capacity. public int InitialCapacity { get { return this.initialCapacity; } } /// /// Gets or sets the periodic removal. /// /// The periodic removal. public TimeSpan PeriodicRemoval { get; set; } /// /// Adds the specified key. /// /// The key. /// The value. public override void Add(TKey key, TValue value) { if (key == null) throw new ArgumentNullException("key"); this.EnsureRemovalOfCollectedEntries(); WeakReference weakKey = new WeakKeyReference(key, this.comparer); WeakReference weakValue = new WeakReference(value); this.internalDictionary.Add(weakKey, weakValue); } /// /// Clears this instance. /// public override void Clear() { this.internalDictionary.Clear(); } /// /// Determines whether the specified key contains key. /// /// The key. /// /// true if the specified key contains key; otherwise, false. /// public override bool ContainsKey(TKey key) { this.EnsureRemovalOfCollectedEntries(); return this.internalDictionary.ContainsKey(key); } /// /// Returns an enumerator that iterates through the collection. /// /// /// An of instance that /// can be used to iterate through the collection. /// public override IEnumerator> GetEnumerator() { this.EnsureRemovalOfCollectedEntries(); foreach (KeyValuePair> pair in this.internalDictionary) { WeakReference weakKey = (WeakReference)pair.Key; WeakReference weakValue = pair.Value; TKey key = weakKey.Target; TValue value = weakValue.Target; if (weakKey.IsAlive && weakValue.IsAlive) yield return new KeyValuePair(key, value); } } /// /// Removes the specified key. /// /// The key. /// /// true if the was successfully /// removed; otherwise, false. /// public override bool Remove(TKey key) { var result = this.internalDictionary.Remove(key); this.EnsureRemovalOfCollectedEntries(); return result; } // Removes the left-over weak references for entries in the dictionary // whose key or value has already been reclaimed by the garbage // collector. This will reduce the dictionary's Count by the number // of dead key-value pairs that were eliminated. /// /// Removes the collected entries. /// public void RemoveCollectedEntries() { List list = null; foreach (KeyValuePair> pair in this.internalDictionary) { WeakReference weakKey = (WeakReference)pair.Key; WeakReference weakValue = pair.Value; if (!weakKey.IsAlive || !weakValue.IsAlive) { if (list == null) list = new List(); list.Add(weakKey); } } if (list != null) { foreach (object key in list) this.internalDictionary.Remove(key); } } /// /// Tries the get value. /// /// The key. /// The value. /// /// true if the was successfully /// found; otherwise, false. /// public override bool TryGetValue(TKey key, out TValue value) { this.EnsureRemovalOfCollectedEntries(); WeakReference weakValue; if (this.internalDictionary.TryGetValue(key, out weakValue)) { value = weakValue.Target; return weakValue.IsAlive; } value = default(TValue); return false; } /// /// Sets the value. /// /// The key. /// The value. protected override void SetValue(TKey key, TValue value) { WeakReference weakKey = new WeakKeyReference(key, this.comparer); this.internalDictionary[weakKey] = new WeakReference(value); } /// /// Ensures the removal of collected entries. /// private void EnsureRemovalOfCollectedEntries() { if (this.PeriodicRemoval != null && this.lastRemoval.Add(this.PeriodicRemoval) < DateTime.Now) { this.RemoveCollectedEntries(); this.lastRemoval = DateTime.Now; } } } }