C # (2)에서 일반적인 양방향 1 대 1 사전 클래스를 찾고 있습니다.
BiDictionaryOneToOne<T, S>
각 값과 키 중 하나만 포함하도록 보장되며 (어쨌든 RefEquals까지) 키 또는 값을 사용하여 검색 할 수 있습니다. 누구나 하나를 알고 있거나 직접 구현해야합니까? 내가 이것을 필요로하는 첫 번째 사람이라고 믿을 수 없다 ...
이 질문이지만 고유 한 요소가 아니며 RemoveByFirst (T t) 또는 RemoveBySecond (S s)도 구현하지 않습니다.
감사합니다!
-
답변 # 1
-
답변 # 2
양방향 사전의보다 완전한 구현 :
원래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 (permalink, search)
복사 :
[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); } }
-
답변 # 3
도움이되는 질문은이 답변에서 일대일 구현을 보여줍니다. 추가 인터페이스 등을 구현하는 것처럼 RemoveByFirst 및 RemoveBySecond를 추가하는 것은 쉽지 않습니다.
-
답변 # 4
허용 된 답변과 동일하지만
Update
를 제공했습니다. 방법뿐만 아니라 조금 더 다듬어도됩니다 :public class BiDictionary<TKey1, TKey2> : IEnumerable<Tuple<TKey1, TKey2>> { Dictionary<TKey1, TKey2> _forwards; Dictionary<TKey2, TKey1> _reverses; public int Count { get { if (_forwards.Count != _reverses.Count) throw new Exception("somewhere logic went wrong and your data got corrupt"); return _forwards.Count; } } public ICollection<TKey1> Key1s { get { return _forwards.Keys; } } public ICollection<TKey2> Key2s { get { return _reverses.Keys; } } public BiDictionary(IEqualityComparer<TKey1> comparer1 = null, IEqualityComparer<TKey2> comparer2 = null) { _forwards = new Dictionary<TKey1, TKey2>(comparer1); _reverses = new Dictionary<TKey2, TKey1>(comparer2); } public bool ContainsKey1(TKey1 key) { return ContainsKey(key, _forwards); } private static bool ContainsKey<S, T>(S key, Dictionary<S, T> dict) { return dict.ContainsKey(key); } public bool ContainsKey2(TKey2 key) { return ContainsKey(key, _reverses); } public TKey2 GetValueByKey1(TKey1 key) { return GetValueByKey(key, _forwards); } private static T GetValueByKey<S, T>(S key, Dictionary<S, T> dict) { return dict[key]; } public TKey1 GetValueByKey2(TKey2 key) { return GetValueByKey(key, _reverses); } public bool TryGetValueByKey1(TKey1 key, out TKey2 value) { return TryGetValue(key, _forwards, out value); } private static bool TryGetValue<S, T>(S key, Dictionary<S, T> dict, out T value) { return dict.TryGetValue(key, out value); } public bool TryGetValueByKey2(TKey2 key, out TKey1 value) { return TryGetValue(key, _reverses, out value); } public bool Add(TKey1 key1, TKey2 key2) { if (ContainsKey1(key1) || ContainsKey2(key2)) // very important return false; AddOrUpdate(key1, key2); return true; } public void AddOrUpdateByKey1(TKey1 key1, TKey2 key2) { if (!UpdateByKey1(key1, key2)) AddOrUpdate(key1, key2); } // dont make this public; a dangerous method used cautiously in this class private void AddOrUpdate(TKey1 key1, TKey2 key2) { _forwards[key1] = key2; _reverses[key2] = key1; } public void AddOrUpdateKeyByKey2(TKey2 key2, TKey1 key1) { if (!UpdateByKey2(key2, key1)) AddOrUpdate(key1, key2); } public bool UpdateKey1(TKey1 oldKey, TKey1 newKey) { return UpdateKey(oldKey, _forwards, newKey, (key1, key2) => AddOrUpdate(key1, key2)); } private static bool UpdateKey<S, T>(S oldKey, Dictionary<S, T> dict, S newKey, Action<S, T> updater) { T otherKey; if (!TryGetValue(oldKey, dict, out otherKey) || ContainsKey(newKey, dict)) return false; Remove(oldKey, dict); updater(newKey, otherKey); return true; } public bool UpdateKey2(TKey2 oldKey, TKey2 newKey) { return UpdateKey(oldKey, _reverses, newKey, (key1, key2) => AddOrUpdate(key2, key1)); } public bool UpdateByKey1(TKey1 key1, TKey2 key2) { return UpdateByKey(key1, _forwards, _reverses, key2, (k1, k2) => AddOrUpdate(k1, k2)); } private static bool UpdateByKey<S, T>(S key1, Dictionary<S, T> forwards, Dictionary<T, S> reverses, T key2, Action<S, T> updater) { T otherKey; if (!TryGetValue(key1, forwards, out otherKey) || ContainsKey(key2, reverses)) return false; if (!Remove(otherKey, reverses)) throw new Exception("somewhere logic went wrong and your data got corrupt"); updater(key1, key2); return true; } public bool UpdateByKey2(TKey2 key2, TKey1 key1) { return UpdateByKey(key2, _reverses, _forwards, key1, (k1, k2) => AddOrUpdate(k2, k1)); } public bool RemoveByKey1(TKey1 key) { return RemoveByKey(key, _forwards, _reverses); } private static bool RemoveByKey<S, T>(S key, Dictionary<S, T> keyDict, Dictionary<T, S> valueDict) { T otherKey; if (!TryGetValue(key, keyDict, out otherKey)) return false; if (!Remove(key, keyDict) || !Remove(otherKey, valueDict)) throw new Exception("somewhere logic went wrong and your data got corrupt"); return true; } private static bool Remove<S, T>(S key, Dictionary<S, T> dict) { return dict.Remove(key); } public bool RemoveByKey2(TKey2 key) { return RemoveByKey(key, _reverses, _forwards); } public void Clear() { _forwards.Clear(); _reverses.Clear(); } public IEnumerator<Tuple<TKey1, TKey2>> GetEnumerator() { if (_forwards.Count != _reverses.Count) throw new Exception("somewhere logic went wrong and your data got corrupt"); foreach (var item in _forwards) yield return Tuple.Create(item.Key, item.Value); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
여기서 내 대답과 비슷합니다
주의해야 할 사항 :
<올>IEnumerable<>
만 구현했습니다. . 나는ICollection<>
를 생각하지 않습니다 이 특별한 콜렉션 구조에 대해 메소드 이름이 모두 다를 수 있기 때문에 여기서 의미가 있습니다.IEnumerable<>
에 들어가야 할 것을 결정하는 것은 당신에게 달려 있습니다. . 이제var p = new BiDictionary<int, string> { 1, "a" }, { 2, "b" } };
데이터 무결성을 위해 이상한 예외가 여기저기서 발생하려고했습니다. 내 코드에 버그가 있는지 알 수 있도록 더 안전한쪽에 있어야합니다.
성능 :
Value
를 검색 할 수 있습니다Keys
중 하나와 이는Get
를 의미합니다. 그리고Contains
이 방법은 1 번만 조회하면됩니다 (O (1)). 와이즈 비즈 조회 2 회와 추가 2 회가 필요합니다. 와이즈 비즈 조회 1 회와 추가 2 회가 필요합니다. 와이즈 비즈 3 번 조회합니다. 모두 정답과 비슷합니다. -
답변 # 5
C5 컬렉션 클래스를 사용하여 이러한 클래스를 만들었습니다.
Add
Update
- c# - 여러 목록의 UNION
- c# - ASPNET MVC 코드 우선 빌드 빈 마이그레이션
- c# - 이 라인에 트랜잭션 범위를 사용해야합니까?
- .net - 텍스트 또는 이미지와 함께 원본 클립 보드를 저장하고 나중에 C #에서 복원
- c# - XML 속성에 네임 스페이스 접두사가 추가되는 이유는 무엇입니까?
- c# - net 4x 및 net 5 앱간에 인증 쿠키 공유 - 사용자 클레임 액세스 원칙
- c# - EF 데이터를 삽입하지만 모든 GUID가 서로 동일하게 바뀝니다
- c# - SemaphoreSlim 사용 제한
- .net - C #에서 메모리를 할당하는 속성에서 List 를 반환합니다
- .net - IEnumerable (C #) 대신 IList에서 데이터 가져 오기
여기, 나의 시도 (Jon 's를 기반으로 구축)는 여기에 보관되어 개선을 위해 열려 있습니다 :