C#中的双向1对1词典

我正在寻找C#(2)中的泛型,双向1对1 Dictionary类,即。 一个BiDictionaryOneToOne<T, S> ,它保证只包含每个值和键中的一个(无论如何都是RefEquals),并且可以使用键或值进行搜索。 任何人都知道一个,或者我应该自己实现它? 我无法相信我是第一个需要这个的人...

在这个问题的答案中有一个BiDictionary,但它不是用于唯一元素(也不实现RemoveByFirst(T t)或RemoveBySecond(S s))。

谢谢!


好的,这里是我的尝试(建立在Jon的基础上 - 谢谢),在这里归档并开放改进:

/// <summary>
/// This is a dictionary guaranteed to have only one of each value and key. 
/// It may be searched either by TFirst or by TSecond, giving a unique answer because it is 1 to 1.
/// </summary>
/// <typeparam name="TFirst">The type of the "key"</typeparam>
/// <typeparam name="TSecond">The type of the "value"</typeparam>
public class BiDictionaryOneToOne<TFirst, TSecond>
{
    IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
    IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

    #region Exception throwing methods

    /// <summary>
    /// Tries to add the pair to the dictionary.
    /// Throws an exception if either element is already in the dictionary
    /// </summary>
    /// <param name="first"></param>
    /// <param name="second"></param>
    public void Add(TFirst first, TSecond second)
    {
        if (firstToSecond.ContainsKey(first) || secondToFirst.ContainsKey(second))
            throw new ArgumentException("Duplicate first or second");

        firstToSecond.Add(first, second);
        secondToFirst.Add(second, first);
    }

    /// <summary>
    /// Find the TSecond corresponding to the TFirst first
    /// Throws an exception if first is not in the dictionary.
    /// </summary>
    /// <param name="first">the key to search for</param>
    /// <returns>the value corresponding to first</returns>
    public TSecond GetByFirst(TFirst first)
    {
        TSecond second;
        if (!firstToSecond.TryGetValue(first, out second))
            throw new ArgumentException("first");

        return second; 
    }

    /// <summary>
    /// Find the TFirst corresponing to the Second second.
    /// Throws an exception if second is not in the dictionary.
    /// </summary>
    /// <param name="second">the key to search for</param>
    /// <returns>the value corresponding to second</returns>
    public TFirst GetBySecond(TSecond second)
    {
        TFirst first;
        if (!secondToFirst.TryGetValue(second, out first))
            throw new ArgumentException("second");

        return first; 
    }


    /// <summary>
    /// Remove the record containing first.
    /// If first is not in the dictionary, throws an Exception.
    /// </summary>
    /// <param name="first">the key of the record to delete</param>
    public void RemoveByFirst(TFirst first)
    {
        TSecond second;
        if (!firstToSecond.TryGetValue(first, out second))
            throw new ArgumentException("first");

        firstToSecond.Remove(first);
        secondToFirst.Remove(second);
    }

    /// <summary>
    /// Remove the record containing second.
    /// If second is not in the dictionary, throws an Exception.
    /// </summary>
    /// <param name="second">the key of the record to delete</param>
    public void RemoveBySecond(TSecond second)
    {
        TFirst first;
        if (!secondToFirst.TryGetValue(second, out first))
            throw new ArgumentException("second");

        secondToFirst.Remove(second);
        firstToSecond.Remove(first);
    }

    #endregion

    #region Try methods

    /// <summary>
    /// Tries to add the pair to the dictionary.
    /// Returns false if either element is already in the dictionary        
    /// </summary>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns>true if successfully added, false if either element are already in the dictionary</returns>
    public Boolean TryAdd(TFirst first, TSecond second)
    {
        if (firstToSecond.ContainsKey(first) || secondToFirst.ContainsKey(second))
            return false;

        firstToSecond.Add(first, second);
        secondToFirst.Add(second, first);
        return true;
    }


    /// <summary>
    /// Find the TSecond corresponding to the TFirst first.
    /// Returns false if first is not in the dictionary.
    /// </summary>
    /// <param name="first">the key to search for</param>
    /// <param name="second">the corresponding value</param>
    /// <returns>true if first is in the dictionary, false otherwise</returns>
    public Boolean TryGetByFirst(TFirst first, out TSecond second)
    {
        return firstToSecond.TryGetValue(first, out second);
    }

    /// <summary>
    /// Find the TFirst corresponding to the TSecond second.
    /// Returns false if second is not in the dictionary.
    /// </summary>
    /// <param name="second">the key to search for</param>
    /// <param name="first">the corresponding value</param>
    /// <returns>true if second is in the dictionary, false otherwise</returns>
    public Boolean TryGetBySecond(TSecond second, out TFirst first)
    {
        return secondToFirst.TryGetValue(second, out first);
    }

    /// <summary>
    /// Remove the record containing first, if there is one.
    /// </summary>
    /// <param name="first"></param>
    /// <returns> If first is not in the dictionary, returns false, otherwise true</returns>
    public Boolean TryRemoveByFirst(TFirst first)
    {
        TSecond second;
        if (!firstToSecond.TryGetValue(first, out second))
            return false;

        firstToSecond.Remove(first);
        secondToFirst.Remove(second);
        return true;
    }

    /// <summary>
    /// Remove the record containing second, if there is one.
    /// </summary>
    /// <param name="second"></param>
    /// <returns> If second is not in the dictionary, returns false, otherwise true</returns>
    public Boolean TryRemoveBySecond(TSecond second)
    {
        TFirst first;
        if (!secondToFirst.TryGetValue(second, out first))
            return false;

        secondToFirst.Remove(second);
        firstToSecond.Remove(first);
        return true;
    }

    #endregion        

    /// <summary>
    /// The number of pairs stored in the dictionary
    /// </summary>
    public Int32 Count
    {
        get { return firstToSecond.Count; }
    }

    /// <summary>
    /// Removes all items from the dictionary.
    /// </summary>
    public void Clear()
    {
        firstToSecond.Clear();
        secondToFirst.Clear();
    }
}

双向字典的更完整实现:

  • 支持原始Dictionary<TKey,TValue> 几乎所有接口 (基础设施接口除外):
  • IDictionary<TKey, TValue>
  • IReadOnlyDictionary<TKey, TValue>
  • IDictionary
  • ICollection<KeyValuePair<TKey, TValue>> (这个和下面是上面的基本接口)
  • ICollection
  • IReadOnlyCollection<KeyValuePair<TKey, TValue>>
  • IEnumerable<KeyValuePair<TKey, TValue>>
  • IEnumerable
  • 序列化使用SerializableAttribute
  • 使用DebuggerDisplayAttribute (带有计数信息)和DebuggerTypeProxyAttribute (用于在手表中显示键值对) 调试视图
  • 反向字典可以作为IDictionary<TValue, TKey> Reverse属性,也可以实现上面提到的所有接口。 对任何字典的所有操作都会修改两者。
  • 用法:

    var dic = new BiDictionary<int, string>();
    dic.Add(1, "1");
    dic[2] = "2";
    dic.Reverse.Add("3", 3);
    dic.Reverse["4"] = 4;
    dic.Clear();
    

    代码在我的GitHub上的私有框架中可用:BiDictionary(TFirst,TSecond).cs(永久链接,搜索)。

    复制:

    [Serializable]
    [DebuggerDisplay ("Count = {Count}"), DebuggerTypeProxy (typeof(DictionaryDebugView<,>))]
    public class BiDictionary<TFirst, TSecond> : IDictionary<TFirst, TSecond>, IReadOnlyDictionary<TFirst, TSecond>, IDictionary
    {
        private readonly IDictionary<TFirst, TSecond> _firstToSecond = new Dictionary<TFirst, TSecond>();
        [NonSerialized]
        private readonly IDictionary<TSecond, TFirst> _secondToFirst = new Dictionary<TSecond, TFirst>();
        [NonSerialized]
        private readonly ReverseDictionary _reverseDictionary;
    
        public BiDictionary ()
        {
            _reverseDictionary = new ReverseDictionary(this);
        }
    
        public IDictionary<TSecond, TFirst> Reverse
        {
            get { return _reverseDictionary; }
        }
    
        public int Count
        {
            get { return _firstToSecond.Count; }
        }
    
        object ICollection.SyncRoot
        {
            get { return ((ICollection)_firstToSecond).SyncRoot; }
        }
    
        bool ICollection.IsSynchronized
        {
            get { return ((ICollection)_firstToSecond).IsSynchronized; }
        }
    
        bool IDictionary.IsFixedSize
        {
            get { return ((IDictionary)_firstToSecond).IsFixedSize; }
        }
    
        public bool IsReadOnly
        {
            get { return _firstToSecond.IsReadOnly || _secondToFirst.IsReadOnly; }
        }
    
        public TSecond this [TFirst key]
        {
            get { return _firstToSecond[key]; }
            set
            {
                _firstToSecond[key] = value;
                _secondToFirst[value] = key;
            }
        }
    
        object IDictionary.this [object key]
        {
            get { return ((IDictionary)_firstToSecond)[key]; }
            set
            {
                ((IDictionary)_firstToSecond)[key] = value;
                ((IDictionary)_secondToFirst)[value] = key;
            }
        }
    
        public ICollection<TFirst> Keys
        {
            get { return _firstToSecond.Keys; }
        }
    
        ICollection IDictionary.Keys
        {
            get { return ((IDictionary)_firstToSecond).Keys; }
        }
    
        IEnumerable<TFirst> IReadOnlyDictionary<TFirst, TSecond>.Keys
        {
            get { return ((IReadOnlyDictionary<TFirst, TSecond>)_firstToSecond).Keys; }
        }
    
        public ICollection<TSecond> Values
        {
            get { return _firstToSecond.Values; }
        }
    
        ICollection IDictionary.Values
        {
            get { return ((IDictionary)_firstToSecond).Values; }
        }
    
        IEnumerable<TSecond> IReadOnlyDictionary<TFirst, TSecond>.Values
        {
            get { return ((IReadOnlyDictionary<TFirst, TSecond>)_firstToSecond).Values; }
        }
    
        public IEnumerator<KeyValuePair<TFirst, TSecond>> GetEnumerator ()
        {
            return _firstToSecond.GetEnumerator();
        }
    
        IEnumerator IEnumerable.GetEnumerator ()
        {
            return GetEnumerator();
        }
    
        IDictionaryEnumerator IDictionary.GetEnumerator ()
        {
            return ((IDictionary)_firstToSecond).GetEnumerator();
        }
    
        public void Add (TFirst key, TSecond value)
        {
            _firstToSecond.Add(key, value);
            _secondToFirst.Add(value, key);
        }
    
        void IDictionary.Add (object key, object value)
        {
            ((IDictionary)_firstToSecond).Add(key, value);
            ((IDictionary)_secondToFirst).Add(value, key);
        }
    
        public void Add (KeyValuePair<TFirst, TSecond> item)
        {
            _firstToSecond.Add(item);
            _secondToFirst.Add(item.Reverse());
        }
    
        public bool ContainsKey (TFirst key)
        {
            return _firstToSecond.ContainsKey(key);
        }
    
        public bool Contains (KeyValuePair<TFirst, TSecond> item)
        {
            return _firstToSecond.Contains(item);
        }
    
        public bool TryGetValue (TFirst key, out TSecond value)
        {
            return _firstToSecond.TryGetValue(key, out value);
        }
    
        public bool Remove (TFirst key)
        {
            TSecond value;
            if (_firstToSecond.TryGetValue(key, out value)) {
                _firstToSecond.Remove(key);
                _secondToFirst.Remove(value);
                return true;
            }
            else
                return false;
        }
    
        void IDictionary.Remove (object key)
        {
            var firstToSecond = (IDictionary)_firstToSecond;
            if (!firstToSecond.Contains(key))
                return;
            var value = firstToSecond[key];
            firstToSecond.Remove(key);
            ((IDictionary)_secondToFirst).Remove(value);
        }
    
        public bool Remove (KeyValuePair<TFirst, TSecond> item)
        {
            return _firstToSecond.Remove(item);
        }
    
        public bool Contains (object key)
        {
            return ((IDictionary)_firstToSecond).Contains(key);
        }
    
        public void Clear ()
        {
            _firstToSecond.Clear();
            _secondToFirst.Clear();
        }
    
        public void CopyTo (KeyValuePair<TFirst, TSecond>[] array, int arrayIndex)
        {
            _firstToSecond.CopyTo(array, arrayIndex);
        }
    
        void ICollection.CopyTo (Array array, int index)
        {
            ((IDictionary)_firstToSecond).CopyTo(array, index);
        }
    
        [OnDeserialized]
        internal void OnDeserialized (StreamingContext context)
        {
            _secondToFirst.Clear();
            foreach (var item in _firstToSecond)
                _secondToFirst.Add(item.Value, item.Key);
        }
    
        private class ReverseDictionary : IDictionary<TSecond, TFirst>, IReadOnlyDictionary<TSecond, TFirst>, IDictionary
        {
            private readonly BiDictionary<TFirst, TSecond> _owner;
    
            public ReverseDictionary (BiDictionary<TFirst, TSecond> owner)
            {
                _owner = owner;
            }
    
            public int Count
            {
                get { return _owner._secondToFirst.Count; }
            }
    
            object ICollection.SyncRoot
            {
                get { return ((ICollection)_owner._secondToFirst).SyncRoot; }
            }
    
            bool ICollection.IsSynchronized
            {
                get { return ((ICollection)_owner._secondToFirst).IsSynchronized; }
            }
    
            bool IDictionary.IsFixedSize
            {
                get { return ((IDictionary)_owner._secondToFirst).IsFixedSize; }
            }
    
            public bool IsReadOnly
            {
                get { return _owner._secondToFirst.IsReadOnly || _owner._firstToSecond.IsReadOnly; }
            }
    
            public TFirst this [TSecond key]
            {
                get { return _owner._secondToFirst[key]; }
                set
                {
                    _owner._secondToFirst[key] = value;
                    _owner._firstToSecond[value] = key;
                }
            }
    
            object IDictionary.this [object key]
            {
                get { return ((IDictionary)_owner._secondToFirst)[key]; }
                set
                {
                    ((IDictionary)_owner._secondToFirst)[key] = value;
                    ((IDictionary)_owner._firstToSecond)[value] = key;
                }
            }
    
            public ICollection<TSecond> Keys
            {
                get { return _owner._secondToFirst.Keys; }
            }
    
            ICollection IDictionary.Keys
            {
                get { return ((IDictionary)_owner._secondToFirst).Keys; }
            }
    
            IEnumerable<TSecond> IReadOnlyDictionary<TSecond, TFirst>.Keys
            {
                get { return ((IReadOnlyDictionary<TSecond, TFirst>)_owner._secondToFirst).Keys; }
            }
    
            public ICollection<TFirst> Values
            {
                get { return _owner._secondToFirst.Values; }
            }
    
            ICollection IDictionary.Values
            {
                get { return ((IDictionary)_owner._secondToFirst).Values; }
            }
    
            IEnumerable<TFirst> IReadOnlyDictionary<TSecond, TFirst>.Values
            {
                get { return ((IReadOnlyDictionary<TSecond, TFirst>)_owner._secondToFirst).Values; }
            }
    
            public IEnumerator<KeyValuePair<TSecond, TFirst>> GetEnumerator ()
            {
                return _owner._secondToFirst.GetEnumerator();
            }
    
            IEnumerator IEnumerable.GetEnumerator ()
            {
                return GetEnumerator();
            }
    
            IDictionaryEnumerator IDictionary.GetEnumerator ()
            {
                return ((IDictionary)_owner._secondToFirst).GetEnumerator();
            }
    
            public void Add (TSecond key, TFirst value)
            {
                _owner._secondToFirst.Add(key, value);
                _owner._firstToSecond.Add(value, key);
            }
    
            void IDictionary.Add (object key, object value)
            {
                ((IDictionary)_owner._secondToFirst).Add(key, value);
                ((IDictionary)_owner._firstToSecond).Add(value, key);
            }
    
            public void Add (KeyValuePair<TSecond, TFirst> item)
            {
                _owner._secondToFirst.Add(item);
                _owner._firstToSecond.Add(item.Reverse());
            }
    
            public bool ContainsKey (TSecond key)
            {
                return _owner._secondToFirst.ContainsKey(key);
            }
    
            public bool Contains (KeyValuePair<TSecond, TFirst> item)
            {
                return _owner._secondToFirst.Contains(item);
            }
    
            public bool TryGetValue (TSecond key, out TFirst value)
            {
                return _owner._secondToFirst.TryGetValue(key, out value);
            }
    
            public bool Remove (TSecond key)
            {
                TFirst value;
                if (_owner._secondToFirst.TryGetValue(key, out value)) {
                    _owner._secondToFirst.Remove(key);
                    _owner._firstToSecond.Remove(value);
                    return true;
                }
                else
                    return false;
            }
    
            void IDictionary.Remove (object key)
            {
                var firstToSecond = (IDictionary)_owner._secondToFirst;
                if (!firstToSecond.Contains(key))
                    return;
                var value = firstToSecond[key];
                firstToSecond.Remove(key);
                ((IDictionary)_owner._firstToSecond).Remove(value);
            }
    
            public bool Remove (KeyValuePair<TSecond, TFirst> item)
            {
                return _owner._secondToFirst.Remove(item);
            }
    
            public bool Contains (object key)
            {
                return ((IDictionary)_owner._secondToFirst).Contains(key);
            }
    
            public void Clear ()
            {
                _owner._secondToFirst.Clear();
                _owner._firstToSecond.Clear();
            }
    
            public void CopyTo (KeyValuePair<TSecond, TFirst>[] array, int arrayIndex)
            {
                _owner._secondToFirst.CopyTo(array, arrayIndex);
            }
    
            void ICollection.CopyTo (Array array, int index)
            {
                ((IDictionary)_owner._secondToFirst).CopyTo(array, index);
            }
        }
    }
    
    internal class DictionaryDebugView<TKey, TValue>
    {
        private readonly IDictionary<TKey, TValue> _dictionary;
    
        [DebuggerBrowsable (DebuggerBrowsableState.RootHidden)]
        public KeyValuePair<TKey, TValue>[] Items
        {
            get
            {
                var array = new KeyValuePair<TKey, TValue>[_dictionary.Count];
                _dictionary.CopyTo(array, 0);
                return array;
            }
        }
    
        public DictionaryDebugView (IDictionary<TKey, TValue> dictionary)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");
            _dictionary = dictionary;
        }
    }
    
    public static class KeyValuePairExts
    {
        public static KeyValuePair<TValue, TKey> Reverse<TKey, TValue> (this KeyValuePair<TKey, TValue> @this)
        {
            return new KeyValuePair<TValue, TKey>(@this.Value, @this.Key);
        }
    }
    

    您提到的问题也显示了此答案中的一对一实现。 添加RemoveByFirst和RemoveBySecond将是微不足道的 - 就像实现额外的接口等一样。

    链接地址: http://www.djcxy.com/p/57837.html

    上一篇: Bidirectional 1 to 1 Dictionary in C#

    下一篇: DB2 backup file deletion