2011-06-20 62 views
10

我以前C++/STL程序員嘗試使用C#/。NET技術編寫一個快速行進算法...查找,或插入只有一個在C#字典查找

我在尋找一個相當於STL方法「map :: insert」,如果不存在則在給定鍵處插入一個值,否則返回現有鍵值對的迭代器。

我發現的唯一辦法做到這一點有兩個查詢:一個內部TryGetValue和另一位在添加方法:

List<Point> list; 
if (!_dictionary.TryGetValue (pcost, out list)) 
{ 
    list = new List<Point>(); 
    dictionary.Add (pcost, list); 
} 
list.Add (new Point { X = n.x, Y = n.y }); 

有什麼解釋了爲什麼這個使用.NET容器是不可能的?或者我錯過了一些觀點?

謝謝。

+1

你確定它甚至在生產代碼中執行兩次查找嗎? – CodingBarfield

+0

雙重查詢真的很重要嗎?時間的差異是微不足道的。 –

+4

@Chris:什麼?你沒有任何基準來支持這一點,更不用說與OP的使用模式相關的基準;) - 我可以告訴你代碼在哪裏重要(哦,等等,我不能出於法律原因...) – sehe

回答

-2

您可以爲創建擴展方法:

IDictionary<string, Point> _dictionary = GetDictionary(); 
_dictionary.GetOrAdd("asdf").Add(new Point(14, 15)); 

// ... elsewhere ... 
public static class DictionaryExtensions { 
    public static List<TValue> GetOrAdd<TKey, TValue>(this IDictionary<TKey, List<TValue>> self, TKey key) { 
     List<TValue> result; 
     self.TryGetValue(key, out result); 
     if (null == result) { 
      // the key value can be set to the null 
      result = new List<TValue>(); 
      self[key] = result; 
     } 

     return result; 
    } 
} 
+1

你假設TValue是一個引用類型,你應該檢查TryGetValue的返回值。 –

+3

這是與現在只作爲擴展名的問題中的代碼相同。這有什麼幫助?還有2個調用,trygetvalue和add。 – RvdK

+0

@PoweRoy:公平的說,表現會受到影響(只是不在正確的方向) – sehe

10

你可以只分配你的價值以下列方式:

var dict = new Dictionary<int, int>(); 
dict[2] = 11; 

如果與鍵2的值不存在 - 它會被添加否則它將被重寫。

字典沒有方法GetOrAdd,但ConcurrentDictionary從C#4.0的作用:

var dict = new ConcurrentDictionary<int, int>(); 
dict[2] = 10; 
int a = dict.GetOrAdd(2, 11);// a == 10 
+0

優秀的信息,gorik – sehe

+0

@gorik使用像你顯示的字典將拋出一個例外,如果密鑰不存在 – gerstla

+3

@amichai gerstl不,請嘗試一下 –

2

標準通用字典不支持此,需要2個查找。雖然查找的成本通常可以忽略不計,所以這不是問題,並且您可以通過調整系統的其他部分來獲得更好的結果,而不是試圖微觀優化字典查找。

唯一支持這個的.net的字典,我知道的是ConcurrentDictionary,方法GetOrAdd。儘管現在您需要支付同步費用。

+0

錯誤,請檢查此答案:http://stackoverflow.com/a/16193323/893406只有一個查找。 –

+1

@ v.oddou對'dict.Add'的調用在內部執行查找,因此2查找起來。一個由TryGetValue查找,一個由Add查找。 –

+0

啊是的。該死的。 –

2

有什麼解釋爲什麼 這是不可能使用.NET 容器?

不知道真實的背景,我認爲這是因爲字典的簡單。只有基本的,易於理解的函數:Add,Remove a.s.o.,而索引運算符有一點魔力,這可能被認爲是直觀的。

2

不幸的是,在bcl的實現中沒有一個。最近的另一種方法是做兩名查找,但可以有一個通用的擴展方法,可以很容易,as shown here

public static T GetOrAdd<S, T>(this IDictionary<S, T> dict, S key, 
           Func<T> valueCreator) 
{ 
    T value; 
    return dict.TryGetValue(key, out value) ? value : dict[key] = valueCreator(); 
} 

但有C5's implementation它做到這一點的開箱。該方法的定義是這樣的:

public virtual bool FindOrAdd(K key, ref V value) 
{ 

} 

我不知道爲什麼他們不接受的Func<V>代替V推遲對象的創建。C5有很多很好的類似的技巧,例如,

public virtual bool Remove(K key, out V value) 

public virtual bool Update(K key, V value, out V oldvalue) 

public virtual bool UpdateOrAdd(K key, V value, out V oldvalue)