C# 手搓一个OrderedDictionary
注意: 粗糙版手账,未经测试,请谨慎使用
Check.NotNull: 别处定义的帮助类方法,与主要功能无关
using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace System.Collections.Generic
{
public class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>
{
private readonly List<TKey> _keys;
private readonly List<TValue> _values;
private readonly IEqualityComparer<TKey> _comparer;
private bool _isReadOnly;
private readonly object _lock = new object();
public OrderedDictionary()
{
_keys = new List<TKey>();
_values = new List<TValue>();
_comparer = EqualityComparer<TKey>.Default;
}
public OrderedDictionary(IEqualityComparer<TKey> comparer)
{
_comparer = comparer ?? EqualityComparer<TKey>.Default;
}
public OrderedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
{
Check.NotNull(collection, nameof(collection));
_keys = collection.Select(x => x.Key).ToList();
_values = collection.Select(x => x.Value).ToList();
_comparer = comparer ?? EqualityComparer<TKey>.Default;
}
public OrderedDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
{
Check.NotNull(dictionary, nameof(dict));
_keys = dictionary.Keys.ToList();
_values = dictionary.Values.ToList();
_comparer = comparer ?? EqualityComparer<TKey>.Default;
}
public ICollection<TKey> Keys => _keys;
public ICollection<TValue> Values => _values;
public IEqualityComparer<TKey> Comparer => _comparer;
public int Count => Keys.Count;
public bool IsReadOnly => _isReadOnly;
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
public TValue this[TKey key]
{
get
{
for (int i = 0; i < Count; i++)
{
if (_comparer.Equals(key, _keys[i]))
{
return _values[i];
}
}
throw new KeyNotFoundException($"The given key `{key}` was not present in the dictionary.");
}
set
{
if (IsReadOnly)
{
throw new InvalidOperationException("The dictionary is read-only.");
}
lock (_lock)
{
bool found = false;
for (int i = 0; i < Count; i++)
{
if (_comparer.Equals(key, _keys[i]))
{
found = true;
_values[i] = value;
}
}
if (!found)
{
Add(key, value);
}
}
}
}
private void CheckReadOnly()
{
if (IsReadOnly)
{
throw new InvalidOperationException("The dictionary is read-only.");
}
}
public void Add(TKey key, TValue value)
{
CheckReadOnly();
lock (_lock)
{
if (ContainsKey(key))
{
throw new ArgumentException($"An item with the same key has already been added. Key: {key}");
}
_keys.Add(key);
_values.Add(value);
}
}
public bool ContainsKey(TKey key)
{
return Keys.Contains(key, _comparer);
}
public bool Remove(TKey key)
{
CheckReadOnly();
lock (_lock)
{
for (int i = 0; i < Count; i++)
{
if (_comparer.Equals(key, _keys[i]))
{
_keys.RemoveAt(i);
_values.RemoveAt(i);
return true;
}
}
return false;
}
}
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
for (int i = 0; i < Count; i++)
{
if (_comparer.Equals(key, _keys[i]))
{
value = _values[i];
return true;
}
}
value = default;
return false;
}
public void Add(KeyValuePair<TKey, TValue> item)
{
Check.NotNull(item, nameof(item));
Add(item.Key, item.Value);
}
public void Clear()
{
CheckReadOnly();
lock (_lock)
{
_keys.Clear();
_values.Clear();
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
Check.NotNull(item, nameof(item));
return ContainsKey(item.Key);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
Check.NotNull(array, nameof(array));
if (arrayIndex > array.Length)
{
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
}
if (array.Length - arrayIndex < Count)
{
throw new ArgumentException("The array is too small.");
}
int count = Count;
for (int i = 0; i < count; i++)
{
array[arrayIndex++] = new KeyValuePair<TKey, TValue>(_keys[i], _values[i]);
}
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
Check.NotNull(item, nameof(item));
return Remove(item.Key);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
for (int i = 0; i < Count; i++)
yield return new KeyValuePair<TKey, TValue>(_keys[i], _values[i]);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}