C#中的双向/双向字典?

Nei*_*ir0 77 .net c# dictionary

我想以下列方式在单词中存储单词:

我可以逐字逐句地获取文字:dict["SomeWord"]- > 123并逐字逐句地获取:dict[123]- >"SomeWord"

这是真的吗?当然,一个办法做到这一点是两点字典:Dictionary<string,int>Dictionary<int,string>,但有另一种方式?

Eni*_*ity 96

我写了几个类,让你做你想做的事.您可能需要使用更多功能扩展它,但这是一个很好的起点.

代码的使用如下所示:

var map = new Map<int, string>();

map.Add(42, "Hello");

Console.WriteLine(map.Forward[42]);
// Outputs "Hello"

Console.WriteLine(map.Reverse["Hello"]);
//Outputs 42
Run Code Online (Sandbox Code Playgroud)

这是定义:

public class Map<T1, T2>
{
    private Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    {
        this.Forward = new Indexer<T1, T2>(_forward);
        this.Reverse = new Indexer<T2, T1>(_reverse);
    }

    public class Indexer<T3, T4>
    {
        private Dictionary<T3, T4> _dictionary;
        public Indexer(Dictionary<T3, T4> dictionary)
        {
            _dictionary = dictionary;
        }
        public T4 this[T3 index]
        {
            get { return _dictionary[index]; }
            set { _dictionary[index] = value; }
        }
    }

    public void Add(T1 t1, T2 t2)
    {
        _forward.Add(t1, t2);
        _reverse.Add(t2, t1);
    }

    public Indexer<T1, T2> Forward { get; private set; }
    public Indexer<T2, T1> Reverse { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不会在异常上维护类不变量.`_forward.Add`有可能成功,而`_reverse.Add`可能会失败,留下部分添加的对. (10认同)
  • @hvd - 就像我说的那样 - 这是一个快速组合的课程. (3认同)
  • @AaA本身并没有修改“ Forward”字典属性(具有“ private set;”),而是通过将其传递到字典的Indexer类的Indexer属性来修改该字典上的值。`公开T4 this [T3索引] {get {return _dictionary [index]; }设置{_dictionary [index] =值;这样就破坏了正向/反向查找。 (3认同)
  • @ Pedro77 - 现在就做了.;-) (2认同)
  • @ Pedro77 - 我只是因为建议我的班级是新的"地图"解决方案而感到厚颜无耻. (2认同)
  • @Aadith - `Indexer <,>`类纯粹是为了允许使用语法为`map.Forward [42]`而不是`map.Forward(42)`.我试图扩展正常的`Dictionary <,>`类,所以我想保留尽可能多的语法. (2认同)
  • @NicholasPetersen类不变量是类的属性,对于类的所有有效实例,它必须始终为true,使用该类的所有代码都可以假定为true,并且使用该类的所有代码必须保持不变.这里有用的类不变量是"对于_forward中的每对(k,v),在_reverse中存在对应的对(v,k)",调用代码肯定会依赖于它.我举了一个简短的例子,说明这个类中的方法如何导致违反该不变量. (2认同)
  • 您可能不希望在Indexer类中使用setter,因为它将破坏前向/反向查找。 (2认同)
  • 如果您编写`myMap.Forward [index] = myValue;`,则只会更新一本字典。反向`myMap.Reverse [myValue] == index;`将为false,因为反向没有值;您可以修复将两个定向程序传递到索引器(以不同顺序)并在设置器上更新两个更新器的问题。 (2认同)

Has*_*oun 11

遗憾的是,您需要两个字典,每个方向一个。但是,您可以使用LINQ轻松获得逆字典:

Dictionary<T1, T2> dict = new Dictionary<T1, T2>();
Dictionary<T2, T1> dictInverse = dict.ToDictionary((i) => i.Value, (i) => i.Key);
Run Code Online (Sandbox Code Playgroud)


Xav*_*ohn 9

通过添加initializes和Contains方法扩展了Enigmativity代码.

public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>>
{
    private readonly Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private readonly Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    {
        Forward = new Indexer<T1, T2>(_forward);
        Reverse = new Indexer<T2, T1>(_reverse);
    }

    public Indexer<T1, T2> Forward { get; private set; }
    public Indexer<T2, T1> Reverse { get; private set; }

    public void Add(T1 t1, T2 t2)
    {
        _forward.Add(t1, t2);
        _reverse.Add(t2, t1);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public IEnumerator<KeyValuePair<T1, T2>> GetEnumerator()
    {
        return _forward.GetEnumerator();
    }

    public class Indexer<T3, T4>
    {
        private readonly Dictionary<T3, T4> _dictionary;

        public Indexer(Dictionary<T3, T4> dictionary)
        {
            _dictionary = dictionary;
        }

        public T4 this[T3 index]
        {
            get { return _dictionary[index]; }
            set { _dictionary[index] = value; }
        }

        public bool Contains(T3 key)
        {
            return _dictionary.ContainsKey(key);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个用例,检查有效的括号

public static class ValidParenthesisExt
{
    private static readonly Map<char, char>
        _parenthesis = new Map<char, char>
        {
            {'(', ')'},
            {'{', '}'},
            {'[', ']'}
        };

    public static bool IsValidParenthesis(this string input)
    {
        var stack = new Stack<char>();
        foreach (var c in input)
        {
            if (_parenthesis.Forward.Contains(c))
                stack.Push(c);
            else
            {
                if (stack.Count == 0) return false;
                if (_parenthesis.Reverse[c] != stack.Pop())
                    return false;
            }
        }
        return stack.Count == 0;
    }
}
Run Code Online (Sandbox Code Playgroud)


zmb*_*mbq 7

你可以使用两个字典,或者如果键和值都是同一类型,你可以使用一个:

TKey并将TValue其用于所有查找.

  • 是的,这个方法在问题中被承认:) (3认同)
  • 这忽略了"键"和"值"中存在相同值的可能性.那么这个解决方案就会发生冲突. (3认同)

Ost*_*ati 6

到底是什么,我会把我的版本混在一起:

public class BijectiveDictionary<TKey, TValue> 
{
    private EqualityComparer<TKey> _keyComparer;
    private Dictionary<TKey, ISet<TValue>> _forwardLookup;
    private EqualityComparer<TValue> _valueComparer;
    private Dictionary<TValue, ISet<TKey>> _reverseLookup;             

    public BijectiveDictionary()
        : this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
    {
    }

    public BijectiveDictionary(EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
        : this(0, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
    {
    }

    public BijectiveDictionary(int capacity, EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
    {
        _keyComparer = keyComparer;
        _forwardLookup = new Dictionary<TKey, ISet<TValue>>(capacity, keyComparer);            
        _valueComparer = valueComparer;
        _reverseLookup = new Dictionary<TValue, ISet<TKey>>(capacity, valueComparer);            
    }

    public void Add(TKey key, TValue value)
    {
        AddForward(key, value);
        AddReverse(key, value);
    }

    public void AddForward(TKey key, TValue value)
    {
        ISet<TValue> values;
        if (!_forwardLookup.TryGetValue(key, out values))
        {
            values = new HashSet<TValue>(_valueComparer);
            _forwardLookup.Add(key, values);
        }
        values.Add(value);
    }

    public void AddReverse(TKey key, TValue value) 
    {
        ISet<TKey> keys;
        if (!_reverseLookup.TryGetValue(value, out keys))
        {
            keys = new HashSet<TKey>(_keyComparer);
            _reverseLookup.Add(value, keys);
        }
        keys.Add(key);
    }

    public bool TryGetReverse(TValue value, out ISet<TKey> keys)
    {
        return _reverseLookup.TryGetValue(value, out keys);
    }

    public ISet<TKey> GetReverse(TValue value)
    {
        ISet<TKey> keys;
        TryGetReverse(value, out keys);
        return keys;
    }

    public bool ContainsForward(TKey key)
    {
        return _forwardLookup.ContainsKey(key);
    }

    public bool TryGetForward(TKey key, out ISet<TValue> values)
    {
        return _forwardLookup.TryGetValue(key, out values);
    }

    public ISet<TValue> GetForward(TKey key)
    {
        ISet<TValue> values;
        TryGetForward(key, out values);
        return values;
    }

    public bool ContainsReverse(TValue value)
    {
        return _reverseLookup.ContainsKey(value);
    }

    public void Clear()
    {
        _forwardLookup.Clear();
        _reverseLookup.Clear();
    }
}
Run Code Online (Sandbox Code Playgroud)

向其中添加一些数据:

var lookup = new BijectiveDictionary<int, int>();

lookup.Add(1, 2);
lookup.Add(1, 3);
lookup.Add(1, 4);
lookup.Add(1, 5);

lookup.Add(6, 2);
lookup.Add(6, 8);
lookup.Add(6, 9);
lookup.Add(6, 10);
Run Code Online (Sandbox Code Playgroud)

然后进行查找:

lookup[2] --> 1, 6
lookup[3] --> 1
lookup[8] --> 6
Run Code Online (Sandbox Code Playgroud)


far*_*121 6

我将 Enigmativity 答案的扩展版本作为 nuget 包提供 https://www.nuget.org/packages/Bi DirectionMap/

这里是开源的


Hac*_*ese 5

您可以使用此扩展方法,尽管它使用枚举,因此对于大型数据集可能没有那么高的性能。如果您担心效率,那么您需要两本词典。如果要将两个字典包装到一个类中,请参阅此问题的公认答案:Bidirectional 1 to 1 Dictionary in C#

public static class IDictionaryExtensions
{
    public static TKey FindKeyByValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TValue value)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");

        foreach (KeyValuePair<TKey, TValue> pair in dictionary)
            if (value.Equals(pair.Value)) return pair.Key;

        throw new Exception("the value is not found in the dictionary");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然这是一个双向字典,但获取 Value 是一个 O(n) 操作,它应该是一个 O(1) 操作。这对于小数据集可能无关紧要,但在处理大数据集时可能会导致性能问题。空间性能的最佳答案是使用两个带有反向数据的字典。 (8认同)