blob: cdc72300700968858753736e2f9c650e670df048 [file] [log] [blame]
// Lucene version compatibility level 4.8.1
using J2N.Collections.Concurrent;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Lucene.Net.Facet.Taxonomy
{
/*
* 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.
*/
/// <summary>
/// <see cref="LruDictionary{TKey, TValue}"/> is similar to of Java's HashMap, which has a bounded <see cref="Limit"/>;
/// When it reaches that <see cref="Limit"/>, each time a new element is added, the least
/// recently used (LRU) entry is removed.
/// <para>
/// Unlike the Java Lucene implementation, this one is thread safe because it is backed by the <see cref="LurchTable{TKey, TValue}"/>.
/// Do note that every time an element is read from <see cref="LruDictionary{TKey, TValue}"/>,
/// a write operation also takes place to update the element's last access time.
/// This is because the LRU order needs to be remembered to determine which element
/// to evict when the <see cref="Limit"/> is exceeded.
/// </para>
/// <para>
///
/// @lucene.experimental
/// </para>
/// </summary>
public class LruDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly LurchTable<TKey, TValue> cache;
/// <summary>
/// Create a new hash map with a bounded size and with least recently
/// used entries removed.
/// </summary>
/// <param name="limit">
/// The maximum size (in number of entries) to which the map can grow
/// before the least recently used entries start being removed.
/// <para/>
/// Setting <paramref name="limit"/> to a very large value, like <see cref="int.MaxValue"/>
/// is allowed, but is less efficient than
/// using <see cref="J2N.Collections.Generic.Dictionary{TKey, TValue}"/> or
/// <see cref="Dictionary{TKey, TValue}"/> because our class needs
/// to keep track of the use order (via an additional doubly-linked
/// list) which is not used when the map's size is always below the
/// maximum size.
/// </param>
public LruDictionary(int limit)
: this(limit, null)
{
}
/// <summary>
/// Create a new hash map with a bounded size and with least recently
/// used entries removed.
/// <para/>
/// LUCENENET specific overload to allow passing in custom <see cref="IEqualityComparer{T}"/>.
/// See LUCENENET-602.
/// </summary>
/// <param name="limit">
/// The maximum size (in number of entries) to which the map can grow
/// before the least recently used entries start being removed.
/// <para/>
/// Setting <paramref name="limit"/> to a very large value, like <see cref="int.MaxValue"/>
/// is allowed, but is less efficient than
/// using <see cref="J2N.Collections.Generic.Dictionary{TKey, TValue}"/> or
/// <see cref="Dictionary{TKey, TValue}"/> because our class needs
/// to keep track of the use order (via an additional doubly-linked
/// list) which is not used when the map's size is always below the
/// maximum size.
/// </param>
/// <param name="comparer">
/// The <see cref="IEqualityComparer{TKey}"/> implementation to use when comparing keys,
/// or <c>null</c> to use the default <see cref="IEqualityComparer{TKey}"/> for the type of the key.
/// </param>
public LruDictionary(int limit, IEqualityComparer<TKey> comparer)
{
cache = new LurchTable<TKey, TValue>(LurchTableOrder.Access, limit, comparer);
}
/// <summary>
/// Allows changing the dictionary's maximal number of elements
/// which was defined at construction time.
/// <para>
/// Note that if the dictionary is already larger than <see cref="Limit"/>, the current
/// implementation does not shrink it (by removing the oldest elements);
/// Rather, the map remains in its current size as new elements are
/// added, and will only start shrinking (until settling again on the
/// given <see cref="Limit"/>) if existing elements are explicitly deleted.
/// </para>
/// </summary>
public virtual int Limit
{
get => cache.Limit;
set
{
if (value < 1)
{
throw new ArgumentOutOfRangeException("Limit must be at least 1");
}
cache.Limit = value;
}
}
public virtual TValue Put(TKey key, TValue value)
{
TValue oldValue = default;
cache.AddOrUpdate(key, value, (k, v) =>
{
oldValue = cache[key];
return value;
});
return oldValue;
}
public virtual TValue Get(TKey key)
{
if (!cache.TryGetValue(key, out TValue result))
{
return default;
}
return result;
}
#region IDictionary<TKey, TValue> members
public virtual TValue this[TKey key]
{
get => cache[key];
set => cache[key] = value;
}
public virtual int Count => cache.Count;
public virtual bool IsReadOnly => false;
public virtual ICollection<TKey> Keys => cache.Keys;
public virtual ICollection<TValue> Values => cache.Values;
public virtual void Add(KeyValuePair<TKey, TValue> item)
{
cache.Add(item.Key, item.Value);
}
public virtual void Add(TKey key, TValue value)
{
cache.Add(key, value);
}
public virtual void Clear()
{
cache.Clear();
}
public virtual bool Contains(KeyValuePair<TKey, TValue> item)
{
return ((ICollection<KeyValuePair<TKey, TValue>>)cache).Contains(item);
}
public virtual bool ContainsKey(TKey key)
{
return cache.ContainsKey(key);
}
public virtual void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{
cache.CopyTo(array, index);
}
public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return cache.GetEnumerator();
}
public virtual bool Remove(KeyValuePair<TKey, TValue> item)
{
return cache.TryRemove(item);
}
public virtual bool Remove(TKey key)
{
return cache.Remove(key);
}
public virtual bool TryGetValue(TKey key, out TValue value)
{
return cache.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}