2013-03-05 92 views
0

我需要一個更堅固的字典狀結構來獲得:通過提供一種鍵(默認字典行爲)IBiLookup和Bilookup:鍵和值「字典」查找

  • 值;
  • 鍵通過提供一個值(不那麼微不足道)。

另外,我想擴大這個字典狀結構的功能,這樣的關鍵可以用多個值關聯。

通過this討論,this答案和this other one提供了實現該工具的工具。我決定從BiDictionary中刪除「通過索引訪問」(Jon Skeet的答案),因爲模糊會比我想要的更頻繁地發生(例如,將字符串映射到字符串時)。我想出了類似字典「結構」是:

using System.Collections.Generic; 

public interface IBiLookup<TLeft, TRight> 
{ 
    IDictionary<TLeft, ICollection<TRight>> LeftToRight { get; } 
    IDictionary<TRight, ICollection<TLeft>> RightToLeft { get; } 

    bool TryGetByLeft(TLeft left, out ICollection<TRight> rights); 
    bool TryGetByRight(TRight right, out ICollection<TLeft> lefts); 

    void Add(TLeft left, TRight right); 
} 
public class BiLookup<TLeft, TRight> : IBiLookup<TLeft, TRight> 
{ 
    public IDictionary<TLeft, ICollection<TRight>> LeftToRight 
    { 
     get { return this.leftToRight; } 
    } 
    public IDictionary<TRight, ICollection<TLeft>> RightToLeft 
    { 
     get { return this.rightToLeft; } 
    } 

    public bool TryGetByLeft(TLeft left, out ICollection<TRight> rights) 
    { 
     return LeftToRight.TryGetValue(left, out rights); 
    } 
    public bool TryGetByRight(TRight right, out ICollection<TLeft> lefts) 
    { 
     return RightToLeft.TryGetValue(right, out lefts); 
    } 

    public void Add(TLeft left, TRight right) 
    { 
     AddLeftToRight(left, right); 
     AddRightToLeft(right, left); 
    } 

    private void AddLeftToRight(TLeft left, TRight right) 
    { 
     ICollection<TRight> rights; 

     // 1) Is there an entry associated with the "left" value? 
     // 2) If so, is the "right" value already associated? 
     if (!TryGetByLeft(left, out rights)) 
     { 
      // Then we have to add an entry in the leftToRight dictionary. 
      rights = new List<TRight> { right };     
     } 
     else 
     { 
      // So there are entries associated with the "left" value. 
      // We must verify if the "right" value itself is not there. 
      if (((List<TRight>)rights).FindIndex(element => element.Equals(right)) < 0) 
      { 
       // We don't have that association yet. 
       rights.Add(right); 
      } 
      else 
      { 
       // The value is already in the list: do nothing. 
       return; 
      } 
     } 

     LeftToRight[left] = rights; 
    } 
    private void AddRightToLeft(TRight right, TLeft left) 
    { 
     ICollection<TLeft> lefts; 

     // 1) Is there an entry associated with the "right" value? 
     // 2) If so, is the "left" value already associated? 
     if (!TryGetByRight(right, out lefts)) 
     { 
      // Then we have to add an entry in the leftToRight dictionary. 
      lefts = new List<TLeft> { left }; 
     } 
     else 
     { 
      // So there are entries associated with the "right" value. 
      // We must verify if the "right" value itself is not there. 
      if (((List<TLeft>)lefts).FindIndex(element => element.Equals(left)) < 0) 
      { 
       // We don't have that association yet. 
       lefts.Add(left); 
      } 
      else 
      { 
       // The value is already in the list: do nothing. 
       return; 
      } 
     } 

     RightToLeft[right] = lefts; 
    } 

    #region Fields 

    private IDictionary<TLeft, ICollection<TRight>> leftToRight = new Dictionary<TLeft, ICollection<TRight>>(); 
    private IDictionary<TRight, ICollection<TLeft>> rightToLeft = new Dictionary<TRight, ICollection<TLeft>>(); 

    #endregion 
} 

我很擔心,如果分割的添加(...)方法分爲二,更具體的方法,是這是一個很好的實現,因爲BiLookup類將被廣泛使用,並且需要注意性能。另外,這個線程的的目的是討論接口和類實現是否儘可能好,或者可以改進它們。

如果我將接口和「deafult」實現同時保存到類庫項目中,那麼設計是否足以重用?

+0

如果您想討論性能,指定需求是一個好主意。它看起來像你不關心添加性能,但希望兩個查找O(1)。 – 2013-03-05 17:20:03

+0

你是對的,但由於需求有些「鬆散」,我只想確保不會爲簡單(和過度使用)的添加(...)操作添加不必要的開銷。 – victorph 2013-03-05 17:23:05

回答

0

如果沒有真正使用它,沒有辦法看到「設計是否足夠重複使用」。你需要嘗試使用幾種不同的方式(所有的單元測試都算作一種),看看它是否有意義,並鼓勵編寫可讀代碼。

如果您沒有任何特別的要求(性能或其他方面)而不是確保您的代碼的單元測試覆蓋率良好,則所有測試都會通過,並且您的預期使用情況將由測試覆蓋。

備註代碼:鑄件(即((List<TLeft>)lefts)看上去不必要的。 FindIndex調用(使用O(number_of_items)進行線性搜索)不會在性能敏感的代碼中查看主頁。

+0

我對自動化測試沒有任何經驗(單元測試,也許?),但只要我的需要是問題,那麼現在代碼看起來已經足夠了。也許我在這裏問的太多了,因爲正如你已經說過的那樣,只有通過使用,才能使代碼符合或不符合要求。我只想確保整體設計沒有太多缺陷,並且還要討論它,因爲有時候會有人要求這種解決方案。關於不必要的演員,你會建議作爲迭代列表的另一種方法? – victorph 2013-03-05 17:59:49

+0

演員:[Any](http://msdn.microsoft.com/en-us/library/bb534972.aspx)將具有相同的性能。當你試圖使用你的代碼時,看看你是否真的需要''有一個很好的機會'IEnumerable '就足夠了(可以通過允許在兩個索引中使用Dictionary來簡化實現)。 – 2013-03-05 18:18:40

+0

謝謝,IEnumerable在這裏更有意義。使用擴展方法任何(System.Linq)部分解決了這個問題,因爲在將元素添加到IEnumerable 之前仍然需要強制轉換。 – victorph 2013-03-05 19:38:21