2016-08-23 3 views
8

저는 인터넷 검색을 계속하고 있는데, 해당 고유 키가있는 변수를 포함하는 목록을 가질 수있는 가장 좋은 방법은 HashTable 또는 입니다. 그러나 자동으로 검색 할 수있는 항목을 찾지 못했습니다. 키 (정수 유형). 사전에 객체 (매개 변수로 전달됨)를 추가하고 자동으로 생성 된 키 (int)를 리턴하고 키 중복이없는 함수를 호출하려고합니다. 내가 어떻게 이걸 이룰 수 있니? 나는 완전히 고투하고있다!자동 사전 키?

편집 : 문제를 명확히하기. 이것은 서버이며 각 클라이언트마다 고유 한 키를 할당하려고합니다. 최대 키 값을 사용하면이 값은 대용량 서버의 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

@Stefan이 말한 것처럼 – 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()를 사용 목록에 추가하면 항상 끝에 추가됩니다. 그래서 이것은 하나의 원자 적 연산으로 add-and-get-index를해야하는 다중 스레드 상황에 빠지지 않는 한 작동합니다. (대체 방법으로 List<T>에 확장 방법을 추가 할 수 있습니다.)

List을 사용할 때의 이점은 키에 간격이 없다는 것입니다. 플립 사이드에서 중간에있는 항목을 제거하면 그 뒤의 모든 항목의 키가 변경됩니다. 그러나 나는 그것이 당신이 찾고있는 행동에 달려 있다고 생각합니다.

+0

나는 똑같은 것을 제안하려했으나 그 위치에서 키가 바뀌지 않았지만 해시 세트가 그 일을 할 것이라고했습니다. – MikeT

+1

목록의 중간에있는 객체가 제거되면이 값 뒤의 모든 객체 인덱스가 1 감소합니다. 그러나, 나는 상수 키가 필요합니다. – None

0

편집에 제공된 추가 정보가 제공되면 int가 올바른 데이터 유형이라고 생각하지 않습니다. ID가있는 클라이언트가 연결이 끊긴 것처럼 보이지만 설명하지 않는 방식으로 ID를 다시 사용해서는 안됩니다. 그 때 당신은 2 개의 클라이언트에 의해 사용중인 1 개의 ID를 가질 수 있음을 깨달으십시오. 귀하의 데이터 유형을 Guid로 변경 한 다음 새 클라이언트를 얻을 때 Guid.NewGuid()의 키와 가능한 중복 된 키가 누락 될 확률 0

0

저는 Stefan Steinegger의 솔루션을 좋아합니다.스레드 안전하지, 분명히

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... 
} 

: 여기에 장면 뒤에 List<>을 사용하는 대안이지만, List<>을 보장은 제거되지 않습니다.

콜렉션에는 동일한 값이 여러 번 나타날 수 있지만 키가 다릅니다.