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();
        }
    }
}
posted @ 2025-02-19 13:51  Theo·Chan  阅读(31)  评论(0)    收藏  举报