2013-12-16 117 views
2

我有添加元素到詞典列表

Dictionary<string, List<int>> myDict = new Dictionary<string, List<int>>(); 

,並在一些點我想號碼添加到myDict特定字典鍵。

我目前在做

if (!myDict.ContainsKey(newKey)){ 
    myDict[newKey] = new List<int>(); 
} 
myDict[newKey].Add(myNumber); 

,但似乎很容易出錯遺忘在某個點上的containsKey檢查。 我已經搜索了一種方法來讓詞典返回一個新的列表,以防MyDict [「entry」]不存在,但我找不到任何東西。

+3

我已經創建了自己的'LazyLookup 中'類,它需要一個值初始化委託並封裝基礎字典。它的行爲與現有的「懶惰」類似。從本質上講,當你試圖訪問一個密鑰時,它會檢查它是否存在,如果沒有,請爲其運行初始化。處理你的確切用法非常方便,特別是如果你發現自己經常這樣做;我從來不需要手動檢查或關心,因爲課程確保爲我初始化。 –

回答

2

這裏是一個相對簡單的實現我所提到的LazyLookup例子。它僅僅爲了回答這個問題而簡潔/簡單地實現IEnumerable

基本上,在訪問索引時,它將確保它已經被初始化爲List<T>類的新實例。

public class LazyLookup<TKey, TValue> : IEnumerable<List<TValue>> 
{ 
    private readonly Dictionary<TKey, List<TValue>> CachedEntries; 
    private readonly Func<List<TValue>> LazyListCreator; 

    public LazyLookup() 
     : this(() => new List<TValue>()) 
    { 

    } 
    public LazyLookup(Func<List<TValue>> lazyListCreator) 
    { 
     this.LazyListCreator = lazyListCreator; 
     this.CachedEntries = new Dictionary<TKey, List<TValue>>(); 
    } 

    public List<TValue> this[TKey key] 
    { 
     get 
     { 
      return GetOrCreateValue(key); 
     } 
    } 

    private List<TValue> GetOrCreateValue(TKey key) 
    { 
     List<TValue> returnValue; 
     if (!CachedEntries.TryGetValue(key, out returnValue)) 
     { 
      returnValue = LazyListCreator(); 
      CachedEntries[key] = returnValue; 
     } 
     return returnValue; 
    } 

    public IEnumerator<List<TValue>> GetEnumerator() 
    { 
     return CachedEntries.Values.GetEnumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

隨着一些用法:

var lazyLookup = new LazyLookup<string, int>(); 

lazyLookup["nocheck"].Add(9001); 

//outputs 9001 
Console.WriteLine(lazyLookup["nocheck"][0]); 

//outputs 0 as it's a newly initialized list 
Console.WriteLine(lazyLookup["someOtherLookup"].Count); 

在這一點上,你可以更新它是線程安全的(如GetOrCreateValue目前線程),或概括它,所以它不會以爲這是的List<T>,但是的任何類型,或擴展它來實現完整的IDictionary<TKey, TValue>接口。但至少,如果您發佈的上述模式經常使用,那麼您可以考慮將直接使用字典的某些封裝替換爲簡化任務並消除代碼重複。

2

您可以使用TryGetValue

List<int> list; 
if(!myDict.TryGetValue(newKey, out list)) 
{ 
    list = new List<int>(); 
    myDict.Add(newKey, list); 
} 
list.Add(myNumber); 

如果Dictionary是場我將封裝在一個方法的存取權限:

Dictionary<string, List<int>> myDict = new Dictionary<string, List<int>>(); 

public void AddNumber(string key, int value) 
{ 
    List<int> list; 
    if(!myDict.TryGetValue(key, out list)) 
    { 
     list = new List<int>(); 
     myDict.Add(key, list); 
    } 
    list.Add(value); 
} 
1

如果使用ConcurrentDictionary<T>,你可以這樣做:

myDict.GetOrAdd(newKey, new List<int>()).Add(myNumber); 
+0

這意味着如果newKey已經存在,每次向我的字典添加內容時我都需要擔心。這是一個更好的語法,但問題仍然存在。 – FlyingFoX

+0

如果您總是使用這種語法進行添加,那麼如果它已經存在,它會將該數字添加到現有列表中。如果它尚未存在,它將爲該密鑰創建一個新的列表,並在其末尾添加「myNumber」。我不確定有比這更清潔的方式。 – Baldrick

1

你實際上可以使用別人的建議。通過在方法中封裝訪問,甚至使用ConcurrentDictionary。

但是對於我來說,我會自定義詞典,所以如果它沒有看到元素,你實際上可以實現myDict["entry"]的功能。

這件事的好處是你完全可以控制你想要這本字典的行爲。

class MyCustomDictionary<TKey, TValue> : IDictionary<TKey, TValue> 
    where TValue : class, new() 
{ 
    private Dictionary<TKey, TValue> _dictionary; 

    public MyCustomDictionary() 
    { 
     _dictionary = new Dictionary<TKey, TValue>(); 
    } 

    public TValue this[TKey key] // this is what's important 
    { 
     get 
     { 
      TValue val; 
      if (!_dictionary.TryGetValue(key, out val)) // if there is no element for that key, add a new element and return it 
      { 
       _dictionary.Add(key, new TValue()); 
       return _dictionary[key]; 
      } 
      else // else return the found element 
      { 
       return val; 
      } 
     } 
     set 
     { 
      _dictionary[key] = value; 
     } 
    } 

    public void Add(TKey key, TValue value) 
    { 
     _dictionary.Add(key, value); 
    } 

    public bool ContainsKey(TKey key) 
    { 
     return _dictionary.ContainsKey(key); 
    } 

    public ICollection<TKey> Keys 
    { 
     get { return _dictionary.Keys; } 
    } 

    public bool Remove(TKey key) 
    { 
     return _dictionary.Remove(key); 
    } 

    public bool TryGetValue(TKey key, out TValue value) 
    { 
     return _dictionary.TryGetValue(key, out value); 
    } 

    public ICollection<TValue> Values 
    { 
     get { return _dictionary.Values; } 
    } 

    public void Add(KeyValuePair<TKey, TValue> item) 
    { 
     _dictionary.Add(item.Key, item.Value); 
    } 

    public void Clear() 
    { 
     _dictionary.Clear(); 
    } 

    public bool Contains(KeyValuePair<TKey, TValue> item) 
    { 
     return _dictionary.Contains(item); 
    } 

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) 
    { 
     _dictionary.ToList().CopyTo(array, arrayIndex); // do you need this? you can leave this :) 
    } 

    public int Count 
    { 
     get { return _dictionary.Count; } 
    } 

    public bool IsReadOnly 
    { 
     get { return false; } 
    } 

    public bool Remove(KeyValuePair<TKey, TValue> item) 
    { 
     return _dictionary.Remove(item.Key); 
    } 

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() 
    { 
     return _dictionary.GetEnumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return _dictionary.GetEnumerator(); 
    } 
} 

然後你使用它像:

MyCustomDictionary<string, List<int>> myCustomDict = new MyCustomDictionary<int, List<int>>(); 
// return a new List of int 
var someElementThatIsNotFound = myCustomDict["keyThatIsNonExistent"]; 
+0

當一個簡單的擴展方法可以滿足併爲任何'IDictionary <>'提供相同的功能時,這是完全矯枉過正的,你的實現也會在getter中查找TWICE,因爲它調用'ContainsKey',然後再次訪問它* ''操作員。 –

+0

我同意。這就是爲什麼我補充說:「好的事情是,你可以完全控制你想要這本字典的行爲。」 :) 我會修改得到的。改用TryGetValue。感謝您看到的那個小優化缺陷。 – aiapatag

0

您可以使用TryGetValue方法:如果有在字典 關鍵你應該添加值到列表中;否則你應該 添加列表與值:

List<int> list 

if (myDict.TryGetValue(newKey, out list)) 
    list.Add(myNumber); 
else 
    myDict.Add(newKey, new List<int>() { myNumber }); 
0

已經有很多很好的答案。我實現了一個擴展方法,具體原因如下:

public static TVALUE GetOrSet<TKEY, TVALUE>(this IDictionary<TKEY, TVALUE> self, 
               TKEY key, 
               Func<TVALUE> defaultValue) 
    { 
     TVALUE value; 
     if (!self.TryGetValue(key, out value)) 
     { 
      value = defaultValue(); 
      self[key] = value; 
     } 
     return value; 
    } // eo GetOrSet 

請注意,如果該值不存在,它將使用一個函數來分配該值。無論哪種方式,該值將被返回。用法:

var dict = new Dictionary<string, List<int>>(); 

List<int> ints = dict.GetOrSet("list1",() => return new List<int>()); 
ints.Add(1); 

如果你沒有再次引用它,你可能會更簡潔:

dict.GetOrSet("list1",() => return new List<int>()).Add(1);