2016-08-23 37 views
8

我保持搜索一段時間,我發現最好的方式,讓你有一個列表包含變量與對應的唯一鍵是HashTableDictionary,但我沒有發現任何東西,讓你有自動鍵(整數類型)。我想調用一個函數,將一個對象(作爲參數傳遞)添加到字典中,並返回自動生成的密鑰(int),並且沒有任何密鑰副本。我怎麼能做到這一點?我完全掙扎!自動字典鍵?

編輯:澄清事情。這是一臺服務器,我想爲每個客戶端分配一個唯一的密鑰。如果我使用最大鍵值,則此值很快會達到大型服務器上的最大值。因爲如果客戶端連接然後斷開連接,他會留下一個應該重新使用的未使用的值,以避免達到非常高的關鍵最大值。

+2

只需用你描述的方法編寫一個包裝字典的類 - 我假設你知道你將如何從你的對象中生成你的密鑰? –

+1

鍵不會從對象中生成,鍵將用於將對象關聯在一起。我試着循環每個鍵,並檢查這個鍵是否等於0,如果鍵0不存在,我會用該鍵創建新的對象,如果存在,我會重複1,2,3,4 ...但我知道這會造成很大的性能問題。 – None

+1

新密鑰是如何產生的?如果它只是自動遞增,那麼你可以包裝一個'List '。 – Lee

回答

5

下應該做的和它重用釋放了鍵:

internal class AutoKeyDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable 
{ 
    private readonly Dictionary<TKey, TValue> inner; 
    private readonly Func<TKey, TKey> incrementor; 
    private readonly Stack<TKey> freeKeys; 
    private readonly TKey keySeed; 
    private TKey currentKey; 

    public AutoKeyDictionary(TKey keySeed, Func<TKey, TKey> incrementor) 
    { 
     if (keySeed == null) 
      throw new ArgumentNullException("keySeed"); 

     if (incrementor == null) 
      throw new ArgumentNullException("incrementor"); 

     inner = new Dictionary<TKey, TValue>(); 
     freeKeys = new Stack<TKey>(); 
     currentKey = keySeed; 
    } 

    public TKey Add(TValue value) //returns the used key 
    { 
     TKey usedKey; 

     if (freeKeys.Count > 0) 
     { 
      usedKey = freeKeys.Pop(); 
      inner.Add(usedKey, value); 
     } 
     else 
     { 
      usedKey = currentKey; 
      inner.Add(usedKey, value); 
      currentKey = incrementor(currentKey); 
     } 

     return usedKey; 
    } 

    public void Clear() 
    { 
     inner.Clear(); 
     freeKeys.Clear(); 
     currentKey = keySeed; 
    } 

    public bool Remove(TKey key) 
    { 
     if (inner.Remove(key)) 
     { 
      if (inner.Count > 0) 
      { 
       freeKeys.Push(key); 
      } 
      else 
      { 
       freeKeys.Clear(); 
       currentKey = keySeed; 
      } 

      return true; 
     } 

     return false; 
    } 

    public bool TryGetValue(TKey key, out TValue value) { return inner.TryGetValue(key, out value); } 
    public TValue this[TKey key] { get {return inner[key];} set{inner[key] = value;} } 
    public bool ContainsKey(TKey key) { return inner.ContainsKey(key); } 
    public bool ContainsValue(TValue value) { return inner.ContainsValue (value); } 
    public int Count { get{ return inner.Count; } } 
    public Dictionary<TKey,TValue>.KeyCollection Keys { get { return inner.Keys; } } 
    public Dictionary<TKey, TValue>.ValueCollection Values { get { return inner.Values; } } 
    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return inner.GetEnumerator(); } 
    IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)inner).GetEnumerator(); } 
} 

聲明:我沒有測試此代碼,它可能有一點重要的幾個pesty錯誤,一般的做法是合理的。

2

創建使用LINQ字典得到最大鍵值的方法,加1,然後使用該作爲價值的關鍵,你想補充,像這樣:

public void AddToMyDictionary(string value) 
{ 
    int NextKey = MyDictionary.Keys.Max() + 1; 
    MyDictionary.Add(NextKey, value); 
} 

顯然,這裏假設你的字典是Dictionary<int, string>,但你明顯可以修改你的目的。

如果要重新使用已刪除的密鑰,請在添加/刪除某些內容時存儲下一個索引。

private int NextKey = 0; 

public int AddToMyDictionary(string value) 
{ 
    int currentKey = NextKey; 
    MyDictionary.Add(currentKey, value); 
    NextKey = MyDictionary.Keys.Max() + 1; 
    return currentKey; 
} 

public void RemoveFromMyDictionary(int key) 
{ 
    MyDictionary.Remove(key); 
    NextKey = key; 
} 
+0

這具有O(n)的複雜性,所以更專門的​​方法可能會更好。 – Lee

+1

問題是某些對象可能被刪除,所以我想在繼續擴展之前使用這些未使用的值。因爲這些數據將通過互聯網發送。 – None

+0

一個顯而易見的解決方案是將自己最大化,作爲一個私人領域。所以添加後,請執行'maxKey ++; MyDictionary.Add(maxKey,value);'。 –

0

這是int Object.GetHashCode()的用途。

+3

哈希碼不需要是唯一的。 –

+1

就像@斯特凡說的 – None

4

寫一個這樣做的類。事情是這樣的:

class AutoIndexDictionary : IEnumerable<Whatever> 
{ 
    private readonly Dictionary<int, Whatever> myDict = new Dictionary<int, Whatever>(); 

    private int currentIndex = 0; 

    public int Add(Whatever item) 
    { 
    var myIndex = currentIndex 
    myDict.Add(myIndex, item); 
    currentIndex ++; 
    return myIndex; 
    } 

    public void Remove(int index) 
    { 
    myDict.Remove(index); 
    } 

    // implement IEnumerable, indexer etc. 
    // ... 
} 
+0

我在這裏寫了相同的代碼。編輯:你可以使當前的索引從外部可讀:'public int CurrentIndex {get;私人設置; } = 0;'。 –

0

那不是一個List你說的話,沒有任何額外的開銷?你稱之爲「唯一的整數鍵」,但在List術語中,這就是所謂的「索引」。

如果你真的想要一個自定義函數添加值和一鍵搞定全部一步到位,你可以從List<T>繼承,像這樣:

class MyCustomList<T> : List<T> 
{ 
    //Not thread-safe 
    public int AddAndGetKey(T valueToAdd) 
    { 
     Add(valueToAdd); 
     return LastIndexOf(valueToAdd); 
    } 
} 

我用LastIndexOf()因爲列表可能包括重複的值並且添加到列表中總是會增加結尾。所以這應該工作,除非你進入多線程情況,你必須在一個原子操作中添加和獲取索引。 (或者,也許你可以添加一個擴展方法到List<T>。)

使用List的好處是密鑰之間不會有間隙。另一方面,移除中間的物品會改變其後每個物品的關鍵。但我想這取決於你在尋找什麼樣的行爲。

+0

我正要提出同樣的事情,但後來我發現一個鍵不會改變位置,但是一個hashset會這樣做 – MikeT

+1

如果刪除列表中間的一個對象,則此值之後的所有對象將索引減少1.但是,我需要不斷的密鑰。 – None

0

鑑於您在編輯中提供的附加信息,我不認爲int是適合您的正確數據類型,您不應該按照您描述的方式重用ID,就好像帶ID的客戶端斷開連接,但不要意識到那麼你可以有2個客戶使用1個ID。改變你的數據類型爲Guid,然後當你得到一個新的客戶端給它一個密鑰Guid.NewGuid()和重複鍵的機會盡可能接近0 0

0

我喜歡Stefan Steinegger的解決方案。下面是一個使用幕後List<>替代,但保證了List<>不會從刪除:

class AutoKeyDictionary<TValue> : IEnumerable<TValue> where TValue : class 
{ 
    readonly List<TValue> list = new List<TValue>(); 

    public int Add(TValue val) 
    { 
    if (val == null) 
     throw new ArgumentNullException(nameof(val), "This collection will not allow null values."); 
    list.Add(val); 
    return list.Count - 1; 
    } 

    public void RemoveAt(int key) 
    { 
    // do not remove ('list.Count' must never decrease), overwrite with null 
    // (consider throwing if key was already removed) 
    list[key] = null; 
    } 

    public TValue this[int key] 
    { 
    get 
    { 
     var val = list[key]; 
     if (val == null) 
     throw new ArgumentOutOfRangeException(nameof(key), "The value with that key is no longer in this collection."); 
     return val; 
    } 
    } 

    public int NextKey => list.Count; 

    public int Count => list.Count(v => v != null); // expensive O(n), Linq 

    public bool ContainsKey(int key) => key >= 0 && key < list.Count && list[key] != null; 

    public TValue TryGetValue(int key) => (key >= 0 && key < list.Count) ? list[key] : null; 

    public void Clear() 
    { 
    for (var i = 0; i < list.Count; ++i) 
     list[i] = null; 
    } 

    public IEnumerator<TValue> GetEnumerator() => list.Where(v => v != null).GetEnumerator(); // Linq 

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 

    public int FirstKeyOf(TValue val) => list.IndexOf(val); 

    public IDictionary<int, TValue> ToDictionary() 
    { 
    var retColl = new SortedList<int, TValue>(list.Count); 
    for (var i = 0; i < list.Count; ++i) 
    { 
     var val = list[i]; 
     if (val != null) 
     retColl.Add(i, val); 
    } 
    return retColl; 
    } 

    // and so on... 
} 

不是線程安全的,很明顯。

請注意,相同的值可以在該集合中多次出現,但具有不同的鍵。