自动字典键?

时间:2016-08-23 13:47:22

标签: c# dictionary hashtable

我一直在谷歌搜索一段时间,我发现使用包含相应唯一键的变量的列表的最佳方法是Failed to compile JSP /jsp/tiles/date.jsp date.jsp:428:28: This attribute is not recognized. id="date" onkeypress="DateCheck(this)" HashTable,但我没有&# 39;找到任何能让你拥有自动键的东西(整数类型)。我想调用一个函数,它将一个对象(作为参数传递)添加到字典中,并返回自动生成的键(int),并且没有任何密钥重复。我怎么能做到这一点?我完全在挣扎!

编辑:澄清事情。这是一个服务器,我想为每个客户端分配一个唯一的密钥。如果我使用最大键值,则此值很快将达到大型服务器上的int最大值。因为如果客户端连接然后断开连接,他会留下一个未使用的值,该值应该被重用,以避免达到非常高的密钥最大值。

7 个答案:

答案 0 :(得分: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(); }
}

免责声明:我没有测试过这段代码,可能会有一些不太重要的问题,一般方法都是合理的。

答案 1 :(得分: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.
  // ...
}

答案 2 :(得分: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;
}

答案 3 :(得分:0)

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

答案 4 :(得分:0)

不会List做你说的话而没有任何额外的开销吗?你称之为&#34;唯一的整数键&#34;,但在List术语中,它简称为&#34;索引&#34;。

如果您真的想要一个自定义函数来添加一个值并一步一步获取一个键,那么您可以继承List<T>,如下所示:

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

我使用LastIndexOf()因为列表可能包含重复值,并且添加到列表总是会添加到结尾。所以这应该有效,除非你遇到多线程情况,你必须在一个原子操作中添加并获取索引。 (或者,您可以在List<T>添加扩展方法。)

使用List的好处是键中没有间隙。另一方面,删除中间的项目会改变其后的每个项目的键。但我想这取决于你正在寻找的行为。

答案 5 :(得分:0)

鉴于您的编辑中提供的其他信息,我不认为int是您正确的数据类型,您不应该像您描述的那样重用ID,就好像具有ID的客户端断开连接但没有意识到那么你可以让2个客户使用1个ID。将您的数据类型更改为Guid然后当您获得新客户端时,为其提供Guid.NewGuid()的密钥,并且重复密钥的可能性尽可能接近0

答案 6 :(得分: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...
}

显然不是线程安全的。

请注意,相同的值可以在集合中多次出现,但使用不同的键。