2013-12-16 98 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);