C #의 양방향 / 양방향 사전?


90

다음과 같은 방법으로 사전에 단어를 저장하고 싶습니다.

단어 별 코드를 얻을 수 있습니다 : dict["SomeWord"]-> 123그리고 단어 별 코드를 얻을 수 있습니다 : dict[123]->"SomeWord"

진짜야? 물론 하나의 방법은 두 가지 사전입니다해야 할 일 : Dictionary<string,int>Dictionary<int,string>하지만 또 다른 방법은 무엇입니까?


2
표준은 O (1) 액세스 두 가지 ... AFAIK : 제공하는 데이터 형 (.NET 4로) 없다

(? 키워드) 또한 양방향 맵은 멀티 쌍방향지도 않는 ..., 추가적인 제한을 부과하지 않는 것이

답변:


110

나는 당신이 원하는 것을 할 수 있도록 몇 가지 간단한 수업을 썼습니다. 더 많은 기능으로 확장해야 할 수도 있지만 좋은 시작점입니다.

코드 사용은 다음과 같습니다.

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 2013

2
@ Pedro77-저는 제 수업이 새로운 "지도"솔루션이라고 제안하면서 건방진 느낌을 받았습니다.
Enigmativity

12
이것은 예외에 대한 클래스 불변성을 유지하지 않습니다. 것이 가능하다 _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; } }그래서 그것은 정방향 / 역방향 조회를 깨는 것입니다.
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

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

    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 동의합니다. 예에서는 "동일한 유형"이 아니라 "다른 유형의"를 의미하는 것으로 보이지만 동의합니다.
Hutch

이로 인해 향후 예상치 못한 결과가 발생할 수 있으며 코드는 리팩토링을 거치게됩니다. 예를 들어 왼쪽과 오른쪽이 겹치기 시작합니다. 성능에 거의 아무것도 추가하지 않습니다.
Vinigas

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

이 확장 메서드는 열거 형을 사용하지만 사용할 수 있으므로 큰 데이터 집합의 경우 성능이 떨어질 수 있습니다. 효율성이 걱정된다면 두 개의 사전이 필요합니다. 두 사전을 하나의 클래스로 래핑하려면이 질문에 대해 허용되는 답변을 참조하십시오. 양방향 일대일 사전 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 (1) 작업이어야하는 O (n) 작업입니다. 이것은 작은 데이터 세트에는 중요하지 않을 수 있지만 큰 데이터 세트로 작업 할 때 성능 문제를 일으킬 수 있습니다. 공간에 대한 성능에 대한 최상의 대답은 데이터가 반전 된 두 개의 사전을 사용하는 것입니다.
Tom

@TomA i는 Tom과 완전히 동의합니다. 진정한 양방향 사전이 필요한 유일한 경우는 100K, 1M + 항목이있을 때이며 스캔이 적은 것은 사실상 NOOP입니다.
Chris Marisic

여전히 컬렉션 이니셜 라이저를 사용할 수 있기 때문에 내 상황 (작은 dict 크기)에 대해이 솔루션을 좋아합니다. 받아 들여진 대답에서 Map <A, B> 컬렉션 이니셜 라이저에서 사용할 수 없다고 생각합니다.
CVertex

@ChrisMarisic, 선포하기에 이상한 것 같습니다. 이 조회가 타이트한 루프에서 호출되면 500 개 미만의 항목으로도 고통을 느낄 것입니다. 또한 비교 테스트 비용에 따라 다릅니다. 나는 당신의 의견과 같은 포괄적 인 진술이 도움이 될 것이라고 생각하지 않습니다.
Lee Campbell

@LeeCampbell 내 포괄적 인 진술은 측정 가능하고 프로파일 링 된 현실에서와 같이 실제 현실에서의 경험을 기반으로합니다. 복잡한 유형을 사전에 대한 키로 사용하려면 내 문제가 아닙니다.
Chris Marisic

1

Bictionary

다음은 각 답변에서 내가 좋아하는 것을 혼합 한 것입니다. 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>모든 문자열이 고유하더라도 만들 수 없다는 것이 맞 습니까?
Marcus Mangelsdorf

@ Merlin2001, 맞습니다. 더 정확하게는 그것으로 정방향 조회를 할 수 없었습니다. 나는 그것을 극복하는 방법에 대해 생각해야 할 것입니다. 컴파일되지만 항상 역방향 인덱서를 먼저 찾으 T1 == T2므로 정방향 조회가 실패합니다. 또한 조회 호출이 모호하기 때문에 기본 인덱서를 재정의 할 수 없습니다. 의 값이의 값과 T1겹칠 수 있으므로이 제약 조건을 추가하고 이전 제약 조건을 제거했습니다 T2.
toddmo

10
반대로 꽤 심각한 성능 문제가 있습니다. 사전은 O (n) 성능 저하로 두 번 검색됩니다. 두 번째 사전을 사용하는 것이 훨씬 빠르며 유형 제약 조건을 제거합니다.
Steve Cooper

@SteveCooper, 아마도 그것을 a로 감싸고 try예외를 KeyNotFoundExceptions.
toddmo

4
@toddmo이 방법으로 속도를 두 배로 높일 수 있습니다. 더 큰 문제는 .First와 .Any 모두 한 번에 한 항목 씩 검색하여 각 항목을 테스트한다는 것입니다. 따라서 1,000,000 개의 항목 목록을 테스트하려면 1 개 요소 목록보다 검색하는 데 1,000,000 배 더 오래 걸립니다. 사전은 훨씬 빠르며 항목을 더 추가해도 속도가 느려지지 않으므로 두 번째 역 사전은 큰 목록에 비해 엄청난 시간을 절약 할 수 있습니다. 관련성이 없을 수도 있지만 테스트 중에 소량의 데이터로 문제가 될 수 있으며 심각한 데이터가있는 실제 서버에서 성능이 저하됩니다.
Steve Cooper

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

Xavier John의 대답의 수정 된 버전으로, 비교자를 정방향 및 역방향으로 가져 오는 추가 생성자가 있습니다. 예를 들어 이것은 대소 문자를 구분하지 않는 키를 지원합니다. 필요한 경우 추가 생성자를 추가하여 정방향 및 역방향 사전 생성자에 추가 인수를 전달할 수 있습니다.

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

키와 값 유형이 동일한 기존 사전을 전달하면 생성자가 어떻게 동작할지 궁금합니다. 뒤로 또는 앞으로 사용할지 여부를 어떻게 해결할 것인가?
Colm Bhandal

0

다음 캡슐화 클래스는 1 개의 사전 인스턴스에서 linq (IEnumerable Extensions)를 사용합니다.

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

예 : this[string Word]. 추가 문제는 일반적인 관행에 해당하지 않는 변수 이름, 코드와 일치하지 않는 주석 ( UInt16UInt32-그 이유 : 주석을 사용하지 마십시오!), 솔루션이 일반적이지 않음, ...
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 .

주어진 다른 답변과 질적으로 크게 다르지 않습니다. 대부분의 답변과 마찬가지로 두 개의 사전을 사용합니다.

이 사전과 지금까지의 다른 답변에 대해 참신한 것은 양방향 사전처럼 동작하는 것이 아니라 단방향 익숙한 사전처럼 동작하고 다음을 사용하여 사전을 동적으로 뒤집을 수 있다는 것입니다. Reverse 속성. 뒤집힌 객체 참조는 얕기 때문에 원래 참조와 동일한 핵심 객체를 수정할 수 있습니다. 따라서 동일한 객체에 대한 두 개의 참조를 가질 수 있지만 그중 하나는 뒤집힌 것입니다.

이 사전에 대해 아마도 고유 한 또 다른 점은 해당 저장소의 테스트 프로젝트에 작성된 일부 테스트가 있다는 것입니다. 실제로 우리가 사용했으며 지금까지 꽤 안정적이었습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.