Двосторонній / двонаправлений словник на C #?


87

Я хочу зберігати слова у словнику наступним чином:

Я можу отримати код слова за словом: dict["SomeWord"]-> 123і отримати слово за словом код: dict[123]->"SomeWord"

Це реально? Звичайно ж один з способів зробити це два словника: Dictionary<string,int>і , Dictionary<int,string>але є інший спосіб?


2
Не існує стандартного (станом на .NET 4) типу даних, який забезпечує O (1) доступ в обидві сторони ... AFAIK :)

Також не те, що двонаправлена ​​карта (ключове слово?) Накладає додаткові обмеження, якщо тільки багатонаправлена ​​карта ...

Відповіді:


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 - Як я вже сказав - це швидко зібраний клас.
Загадковість

3
@AaA Він не змінює Forwardвластивість словника самостійно (що має private set;), але змінює значення цього словника через властивість Indexer класу Indexer, яке передає його до словника. public T4 this[T3 index] { get { return _dictionary[index]; } set { _dictionary[index] = value; } }Отже, це переривання пошуку вперед / назад.
Jeroen van Langen

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"

Таким чином, єдиний словник може бути використаний для будь-якого типу пошуку.


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 >>.
Остаті

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

8
Хоча це двонаправлений словник, отримання значення - це операція O (n), де вона повинна бути операцією O (1). Це може не мати значення для невеликих наборів даних, але може спричинити проблеми з продуктивністю при роботі з великими. Найкращою відповіддю для продуктивності в просторі було б використання двох словників із зворотними даними.
Том

@TomA Я повністю збігаюся з Томом, єдиний приклад, коли вам потрібен справжній двонаправлений словник, це коли у вас є 100K, 1M + записів, і все менше сканування це фактично NOOP.
Кріс Марісіч

Мені подобається це рішення для моєї ситуації (невеликі розміри dict), оскільки я все ще можу використовувати ініціалізатори колекції. Карта <A, B> у прийнятій відповіді, я не думаю, що її можна використовувати в ініціалізаторах колекції.
CVertex

@ChrisMarisic, це здається дивним для проголошення. Якби цей пошук був викликаний в щільному циклі, я впевнений, ви відчували б біль навіть при <500 записів. Це також залежить від вартості порівняльних тестів. Я не вважаю, що такі обширні заяви, як ваш коментар, корисні.
Lee Campbell

@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


Дуже елегантне рішення! Не могли б ви пояснити глибину цього трохи далі? Я правий, що ти не можеш скласти Bictionary<string, string>навіть, якщо всі рядки унікальні?
Маркус Мангельсдорф,

@ Merlin2001, це правильно. Точніше, з цим ви не могли б робити перегляди вперед. Мені доведеться подумати, як це подолати. Він компілюється, але завжди завжди спочатку знаходить зворотний індексатор T1 == T2, тому прямі пошуки не вдаються. Крім того, я не можу замінити індексатор за замовчуванням, оскільки тоді виклики пошуку будуть неоднозначними. Я додав це обмеження і видалив попереднє, оскільки значення T1можуть перекриватися зі значеннями T2.
toddmo

10
На звороті є досить серйозна проблема продуктивності; пошук у словнику здійснюється двічі, з O (n) результативним натисканням; було б набагато швидше скористатися другим словником і зняти обмеження типу.
Стів Купер

@SteveCooper, можливо, я міг би позбутися враження продуктивності, обернувши його в tryта перетворивши винятки на KeyNotFoundExceptions.
toddmo

4
@toddmo, таким чином, ви можете подвоїти швидкість. Більша проблема полягає в тому, що обидва. Перший і. Будь-який пошук по одному елементу за раз, тестуючи кожен. Отже, для тестування 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();
    }
}

Цікаво, як буде поводитися конструктор, якщо ви передасте йому існуючий словник, де типи і значення значень однакові. Як це вирішить, чи використовувати зворотні проти нападаючих?
Колм Бхандал,

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]. Додатковими проблемами є імена змінних, що не відповідають загальноприйнятій практиці, коментар, невідповідний коду ( UInt16проти 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();
    }
}


0

У BijectionDictionaryцьому репозиторії з відкритим кодом доступний тип:

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

Якісно це не сильно відрізняється від інших наведених відповідей. Він використовує два словники, як і більшість з цих відповідей.

Що нового, я вважаю, щодо цього словника порівняно з іншими відповідями на даний момент, полягає в тому, що замість того, щоб поводитися як двосторонній словник, він просто поводиться як односторонній, звичний словник, а потім динамічно дозволяє гортати словник за допомогою зворотне властивість. Перевернуте посилання на об'єкт неглибоке, тому воно все одно зможе змінити той самий основний об'єкт, що і вихідне посилання. Отже, ви можете мати два посилання на один і той же об’єкт, за винятком того, що одне з них перевернуто.

Інша річ, яка, ймовірно, унікальна щодо цього словника, полягає в тому, що для нього в тестовому проекті під цим репо є кілька тестів. Він використовується нами на практиці і дотепер був досить стабільним.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.