Двусторонний / двунаправленный словарь в C #?

87

Я хочу хранить слова в словаре следующим образом:

Я могу получить код за словом: dict["SomeWord"]-> 123и получить код за словом: dict[123]->"SomeWord"

Это реально? Конечно же один из способов сделать это два словаря: Dictionary<string,int>и , Dictionary<int,string>но есть другой способ?

Neir0
источник
2
Не существует стандартного (
Также не то, чтобы двунаправленная карта (ключевое слово?) Налагает дополнительные ограничения, если только двунаправленная карта ...

Ответы:

109

Я написал пару коротких уроков, которые позволят вам делать то, что вы хотите. Возможно, вам потребуется расширить его дополнительными функциями, но это хорошая отправная точка.

Использование кода выглядит так:

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

map.Add(42, "Hello");

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

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

Вот определение:

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; }
}
Загадочность
источник
2
@ Pedro77 - Теперь это так. ;-)
Enigmativity
2
@ Pedro77 - Я просто дерзко предположил, что мой класс - это новое «картографическое» решение.
Enigmativity
11
Это не поддерживает инварианты классов для исключений. Есть возможность _forward.Addдобиться успеха или _reverse.Addпотерпеть неудачу, оставив вам частично добавленную пару.
5
@hvd - Как я уже сказал - это быстро составляемый класс.
Enigmativity
3
@AaA Он не изменяет Forwardсобственное свойство словаря (которое имеет private set;), но изменяет значение в этом словаре через свойство Indexer класса Indexer, которое передает его в словарь. public T4 this[T3 index] { get { return _dictionary[index]; } set { _dictionary[index] = value; } }Итак, это нарушает прямой / обратный поиск.
Йерун ван Ланген
27

К сожалению, вам понадобится два словаря, по одному на каждое направление. Однако вы можете легко получить обратный словарь с помощью LINQ:

Dictionary<T1, T2> dict = new Dictionary<T1, T2>();
Dictionary<T2, T1> dictInverse = dict.ToDictionary((i) => i.Value, (i) => i.Key);
Хасан Байдун
источник
11

Расширен код Enigmativity, добавив методы инициализации и Contains.

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);
    }

    public void Remove(T1 t1)
    {
        T2 revKey = Forward[t1];
        _forward.Remove(t1);
        _reverse.Remove(revKey);
    }
    
    public void Remove(T2 t2)
    {
        T1 forwardKey = Reverse[t2];
        _reverse.Remove(t2);
        _forward.Remove(forwardKey);
    }

    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);
        }
    }
}

Вот пример использования, проверьте правильность скобок

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;
    }
}
Ксавье Джон
источник
7

Вы можете использовать два словаря, как говорили другие, но также обратите внимание, что если оба TKeyи TValueимеют один и тот же тип (и известно, что их домены значений времени выполнения не пересекаются), вы можете просто использовать один и тот же словарь, создав две записи для каждого ключа / пара значений:

dict["SomeWord"]= "123" а также dict["123"]="SomeWord"

Таким образом, для любого типа поиска можно использовать один словарь.

zmbq
источник
3
Да, этот подход был подтвержден в вопросе :)
3
Это игнорирует возможность того, что одно и то же значение существует как в «ключах», так и в «значениях». тогда он будет конфликтовать в этом решении.
user1028741
1
@ user1028741 Согласен, хотя из примера видно, что они имели в виду «другого типа», а не «того же типа»
Хатч
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();
    }
}

Добавьте к нему данные:

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);

А затем выполните поиск:

lookup[2] --> 1, 6
lookup[3] --> 1
lookup[8] --> 6
Остати
источник
Мне нравится, что это поддерживает 1: N
Себастьян
@Sebastian, вы можете добавить IEnumerable <KeyValuePair <TKey, TValue >>.
Ostati
4

Вы можете использовать этот метод расширения, хотя он использует перечисление и, следовательно, может быть не таким производительным для больших наборов данных. Если вас беспокоит эффективность, то вам понадобятся два словаря. Если вы хотите объединить два словаря в один класс, см. Принятый ответ на этот вопрос: Двунаправленный словарь 1 к 1 в 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");
    }
}
moribvndvs
источник
8
Хотя это двунаправленный словарь, выборка значения - это операция O (n), а должна быть операция O (1). Это может не иметь значения для небольших наборов данных, но может вызвать проблемы с производительностью при работе с большими. Лучшим ответом на производительность в пространстве было бы использование двух словарей с перевернутыми данными.
Tom
@TomA Я полностью согласен с Томом, единственный случай, когда вам нужен настоящий двунаправленный словарь, - это когда у вас есть 100K, 1M + записей, все, что меньше сканирования, фактически является NOOP.
Крис Марисич,
Мне нравится это решение для моей ситуации (небольшие размеры dict), потому что я все еще могу использовать инициализаторы коллекции. Карта <A, B> в принятом ответе, я не думаю, может использоваться в инициализаторах коллекции.
CVertex 05
@ChrisMarisic, это кажется странным. Бьюсь об заклад, если бы этот поиск был вызван в замкнутом цикле, вы бы почувствовали боль даже при <500 записях. Также это зависит от стоимости сравнительных тестов. Я не думаю, что такие резкие заявления, как ваш комментарий, полезны.
Ли Кэмпбелл
@LeeCampbell, мои радикальные утверждения основаны на опыте реальной реальности, такой как измеряемая и профилированная реальность. Если вы хотите использовать какой-то сложный тип как ключ к словарю, это ваша проблема, а не моя проблема.
Крис Марисич
1

Словарь

Вот что мне понравилось в каждом ответе. Он реализует IEnumerableтак, чтобы он мог использовать инициализатор коллекции, как вы можете видеть в примере.

Ограничение использования:

  • Вы используете разные типы данных. (т.е. )T1T2

Код:

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        Bictionary<string, int> bictionary = 
            new Bictionary<string,int>() {
                { "a",1 }, 
                { "b",2 }, 
                { "c",3 } 
            };

        // test forward lookup
        Console.WriteLine(bictionary["b"]);
        // test forward lookup error
        //Console.WriteLine(bictionary["d"]);
        // test reverse lookup
        Console.WriteLine(bictionary[3]); 
        // test reverse lookup error (throws same error as forward lookup does)
        Console.WriteLine(bictionary[4]); 
    }
}

public class Bictionary<T1, T2> : Dictionary<T1, T2>
{
    public T1 this[T2 index]
    {
        get
        {
            if(!this.Any(x => x.Value.Equals(index)))
               throw new System.Collections.Generic.KeyNotFoundException();
            return this.First(x => x.Value.Equals(index)).Key;
        }
    }
}

Скрипка:

https://dotnetfiddle.net/mTNEuw

toddmo
источник
Очень элегантное решение! Не могли бы вы объяснить это чуть подробнее? Я прав, что вы не можете сделать, Bictionary<string, string>даже если все строки уникальны?
Маркус Мангельсдорф,
@ Merlin2001, правильно. Точнее, с этим нельзя было делать упреждающий поиск. Мне нужно будет подумать, как это преодолеть. Он компилируется, но всегда сначала находит обратный индексатор T1 == T2, поэтому прямой поиск не выполняется. Кроме того, я не могу переопределить индексатор по умолчанию, потому что тогда вызовы поиска будут неоднозначными. Я добавил это ограничение и удалил предыдущее, потому что значения T1могут перекрываться со значениями T2.
toddmo
10
На обратной стороне довольно серьезная проблема с производительностью; поиск по словарю выполняется дважды, производительность достигает O (n); было бы намного быстрее использовать второй словарь и снять ограничение типа.
Стив Купер
@SteveCooper, может быть, я смогу избавиться от снижения производительности, заключив его в a tryи преобразовав исключения в KeyNotFoundExceptions.
toddmo
4
@toddmo, вы могли бы таким образом удвоить скорость. Более серьезная проблема заключается в том, что и .First, и .Any ищут по одному элементу за раз, проверяя каждый из них. Таким образом, для проверки списка из 1 000 000 элементов поиск занимает в 1 000 000 раз больше времени, чем для поиска из 1-элементного списка. Словари работают намного быстрее и не замедляются при добавлении новых элементов, поэтому второй обратный словарь сэкономит огромное количество времени при просмотре больших списков. Это может быть не актуально, но это то, что может быть нормально с небольшими объемами данных во время тестирования, а затем убивает производительность на реальном сервере с серьезными данными.
Стив Купер,
1

Это старая проблема, но я хотел добавить два метода расширения на случай, если кто-то сочтет это полезным. Второй вариант не так полезен, но он дает отправную точку, если требуется поддержка индивидуальных словарей.

    public static Dictionary<VALUE,KEY> Inverse<KEY,VALUE>(this Dictionary<KEY,VALUE> dictionary)
    {
        if (dictionary==null || dictionary.Count == 0) { return null; }

        var result = new Dictionary<VALUE, KEY>(dictionary.Count);

        foreach(KeyValuePair<KEY,VALUE> entry in dictionary)
        {
            result.Add(entry.Value, entry.Key);
        }

        return result;
    }

    public static Dictionary<VALUE, KEY> SafeInverse<KEY, VALUE>(this Dictionary<KEY, VALUE> dictionary)
    {
        if (dictionary == null || dictionary.Count == 0) { return null; }

        var result = new Dictionary<VALUE, KEY>(dictionary.Count);

        foreach (KeyValuePair<KEY, VALUE> entry in dictionary)
        {
            if (result.ContainsKey(entry.Value)) { continue; }

            result.Add(entry.Value, entry.Key);
        }

        return result;
    }
Фелипе Рамос
источник
1

Модифицированная версия ответа Ксавьера Джона с дополнительным конструктором для прямого и обратного сравнения. Например, это будет поддерживать ключи без учета регистра. При необходимости можно добавить другие конструкторы для передачи дополнительных аргументов конструкторам прямого и обратного словаря.

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

    /// <summary>
    /// Constructor that uses the default comparers for the keys in each direction.
    /// </summary>
    public Map()
        : this(null, null)
    {
    }

    /// <summary>
    /// Constructor that defines the comparers to use when comparing keys in each direction.
    /// </summary>
    /// <param name="t1Comparer">Comparer for the keys of type T1.</param>
    /// <param name="t2Comparer">Comparer for the keys of type T2.</param>
    /// <remarks>Pass null to use the default comparer.</remarks>
    public Map(IEqualityComparer<T1> t1Comparer, IEqualityComparer<T2> t2Comparer)
    {
        _forward = new Dictionary<T1, T2>(t1Comparer);
        _reverse = new Dictionary<T2, T1>(t2Comparer);
        Forward = new Indexer<T1, T2>(_forward);
        Reverse = new Indexer<T2, T1>(_reverse);
    }

    // Remainder is the same as Xavier John's answer:
    // https://stackoverflow.com/a/41907561/216440
    ...
}

Пример использования с ключом без учета регистра:

Map<int, string> categories = 
new Map<int, string>(null, StringComparer.CurrentCultureIgnoreCase)
{
    { 1, "Bedroom Furniture" },
    { 2, "Dining Furniture" },
    { 3, "Outdoor Furniture" }, 
    { 4, "Kitchen Appliances" }
};

int categoryId = 3;
Console.WriteLine("Description for category ID {0}: '{1}'", 
    categoryId, categories.Forward[categoryId]);

string categoryDescription = "DINING FURNITURE";
Console.WriteLine("Category ID for description '{0}': {1}", 
    categoryDescription, categories.Reverse[categoryDescription]);

categoryDescription = "outdoor furniture";
Console.WriteLine("Category ID for description '{0}': {1}", 
    categoryDescription, categories.Reverse[categoryDescription]);

// Results:
/*
Description for category ID 3: 'Outdoor Furniture'
Category ID for description 'DINING FURNITURE': 2
Category ID for description 'outdoor furniture': 3
*/
Саймон Тевси
источник
1

Вот мой код. Все равно O (1), кроме засеянных конструкторов.

using System.Collections.Generic;
using System.Linq;

public class TwoWayDictionary<T1, T2>
{
    Dictionary<T1, T2> _Forwards = new Dictionary<T1, T2>();
    Dictionary<T2, T1> _Backwards = new Dictionary<T2, T1>();

    public IReadOnlyDictionary<T1, T2> Forwards => _Forwards;
    public IReadOnlyDictionary<T2, T1> Backwards => _Backwards;

    public IEnumerable<T1> Set1 => Forwards.Keys;
    public IEnumerable<T2> Set2 => Backwards.Keys;


    public TwoWayDictionary()
    {
        _Forwards = new Dictionary<T1, T2>();
        _Backwards = new Dictionary<T2, T1>();
    }

    public TwoWayDictionary(int capacity)
    {
        _Forwards = new Dictionary<T1, T2>(capacity);
        _Backwards = new Dictionary<T2, T1>(capacity);
    }

    public TwoWayDictionary(Dictionary<T1, T2> initial)
    {
        _Forwards = initial;
        _Backwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    }

    public TwoWayDictionary(Dictionary<T2, T1> initial)
    {
        _Backwards = initial;
        _Forwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    }


    public T1 this[T2 index]
    {
        get => _Backwards[index];
        set
        {
            if (_Backwards.TryGetValue(index, out var removeThis))
                _Forwards.Remove(removeThis);

            _Backwards[index] = value;
            _Forwards[value] = index;
        }
    }

    public T2 this[T1 index]
    {
        get => _Forwards[index];
        set
        {
            if (_Forwards.TryGetValue(index, out var removeThis))
                _Backwards.Remove(removeThis);

            _Forwards[index] = value;
            _Backwards[value] = index;
        }
    }

    public int Count => _Forwards.Count;

    public bool Contains(T1 item) => _Forwards.ContainsKey(item);
    public bool Contains(T2 item) => _Backwards.ContainsKey(item);

    public bool Remove(T1 item)
    {
        if (!this.Contains(item))
            return false;

        var t2 = _Forwards[item];

        _Backwards.Remove(t2);
        _Forwards.Remove(item);

        return true;
    }

    public bool Remove(T2 item)
    {
        if (!this.Contains(item))
            return false;

        var t1 = _Backwards[item];

        _Forwards.Remove(t1);
        _Backwards.Remove(item);

        return true;
    }

    public void Clear()
    {
        _Forwards.Clear();
        _Backwards.Clear();
    }
}
Iamsodarncool
источник
Интересно, как будет вести себя конструктор, если вы передадите ему существующий словарь, в котором типы ключей и значений совпадают. Как он решит, использовать ли обратные или прямые?
Колм Бхандал,
0

Следующий инкапсулирующий класс использует linq (IEnumerable Extensions) для 1 экземпляра словаря.

public class TwoWayDictionary<TKey, TValue>
{
    readonly IDictionary<TKey, TValue> dict;
    readonly Func<TKey, TValue> GetValueWhereKey;
    readonly Func<TValue, TKey> GetKeyWhereValue;
    readonly bool _mustValueBeUnique = true;

    public TwoWayDictionary()
    {
        this.dict = new Dictionary<TKey, TValue>();
        this.GetValueWhereKey = (strValue) => dict.Where(kvp => Object.Equals(kvp.Key, strValue)).Select(kvp => kvp.Value).FirstOrDefault();
        this.GetKeyWhereValue = (intValue) => dict.Where(kvp => Object.Equals(kvp.Value, intValue)).Select(kvp => kvp.Key).FirstOrDefault();
    }

    public TwoWayDictionary(KeyValuePair<TKey, TValue>[] kvps)
        : this()
    {
        this.AddRange(kvps);
    }

    public void AddRange(KeyValuePair<TKey, TValue>[] kvps)
    {
        kvps.ToList().ForEach( kvp => {        
            if (!_mustValueBeUnique || !this.dict.Any(item => Object.Equals(item.Value, kvp.Value)))
            {
                dict.Add(kvp.Key, kvp.Value);
            } else {
                throw new InvalidOperationException("Value must be unique");
            }
        });
    }

    public TValue this[TKey key]
    {
        get { return GetValueWhereKey(key); }
    }

    public TKey this[TValue value]
    {
        get { return GetKeyWhereValue(value); }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var dict = new TwoWayDictionary<string, int>(new KeyValuePair<string, int>[] {
            new KeyValuePair<string, int>(".jpeg",100),
            new KeyValuePair<string, int>(".jpg",101),
            new KeyValuePair<string, int>(".txt",102),
            new KeyValuePair<string, int>(".zip",103)
        });


        var r1 = dict[100];
        var r2 = dict[".jpg"];

    }

}
Бретт Касвелл
источник
0

Это использует индексатор для обратного поиска.
Обратный поиск - O (n), но он также не использует два словаря.

public sealed class DictionaryDoubleKeyed : Dictionary<UInt32, string>
{   // used UInt32 as the key as it has a perfect hash
    // if most of the lookup is by word then swap
    public void Add(UInt32 ID, string Word)
    {
        if (this.ContainsValue(Word)) throw new ArgumentException();
        base.Add(ID, Word);
    }
    public UInt32 this[string Word]
    {   // this will be O(n)
        get
        {
            return this.FirstOrDefault(x => x.Value == Word).Key;
        }
    } 
}
папарацци
источник
Например: возможен NRE в this[string Word]. Дополнительные проблемы - это имена переменных, не соответствующие общепринятой практике, комментарии, несовместимые с кодом ( UInt16vs UInt32- вот почему: не используйте комментарии!), Решение не является общим, ...
BartoszKP
0

Вот альтернативное решение тем, что были предложены. Удален внутренний класс и обеспечена согласованность при добавлении / удалении элементов.

using System.Collections;
using System.Collections.Generic;

public class Map<E, F> : IEnumerable<KeyValuePair<E, F>>
{
    private readonly Dictionary<E, F> _left = new Dictionary<E, F>();
    public IReadOnlyDictionary<E, F> left => this._left;
    private readonly Dictionary<F, E> _right = new Dictionary<F, E>();
    public IReadOnlyDictionary<F, E> right => this._right;

    public void RemoveLeft(E e)
    {
        if (!this.left.ContainsKey(e)) return;
        this._right.Remove(this.left[e]);
        this._left.Remove(e);
    }

    public void RemoveRight(F f)
    {
        if (!this.right.ContainsKey(f)) return;
        this._left.Remove(this.right[f]);
        this._right.Remove(f);
    }

    public int Count()
    {
        return this.left.Count;
    }

    public void Set(E left, F right)
    {
        if (this.left.ContainsKey(left))
        {
            this.RemoveLeft(left);
        }
        if (this.right.ContainsKey(right))
        {
            this.RemoveRight(right);
        }
        this._left.Add(left, right);
        this._right.Add(right, left);
    }


    public IEnumerator<KeyValuePair<E, F>> GetEnumerator()
    {
        return this.left.GetEnumerator();
    }

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

sirpaddow
источник
0

В BijectionDictionaryэтом репозитории с открытым исходным кодом доступен тип:

https://github.com/ColmBhandal/CsharpExtras .

Качественно он не сильно отличается от других приведенных ответов. Он использует два словаря, как и большинство этих ответов.

Я считаю, что новизна этого словаря по сравнению с другими ответами до сих пор заключается в том, что вместо того, чтобы вести себя как двусторонний словарь, он просто ведет себя как односторонний знакомый словарь, а затем динамически позволяет вам переворачивать словарь, используя Обратное свойство. Ссылка на перевернутый объект неглубокая, поэтому она все равно сможет изменить тот же основной объект, что и исходная ссылка. Таким образом, у вас может быть две ссылки на один и тот же объект, за исключением того, что одна из них перевернута.

Еще одна вещь, которая, вероятно, уникальна в этом словаре, заключается в том, что для него написано несколько тестов в тестовом проекте в этом репо. Он использовался нами на практике и пока работает довольно стабильно.

Колм Бхандал
источник