2009-10-12 2 views
30

삽입 순서를 유지하는 HashSet이 필요합니다. 프레임 워크에이 구현이 있습니까?순서를 유지하는 HashSet

+2

항목 당 메모리의 8 개의 바이트를 소모에 비해 20 % 느린 갖는다 당신은 자바의 [LinkedHashSet] (http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashSet.html)과 같은 뜻이다 – thejoshwolfe

+3

예 mplest 할 일은 연결된리스트와 해쉬 셋을 함께 감싸는 것입니다. LRU 구현에 유용함 –

+1

Set의 정의에 따라 순서를 유지하면 안됩니다. –

답변

21

표준 .NET HashSet은 삽입 순서를 유지하지 않습니다. 간단한 테스트의 경우 삽입 주문은 사고로 인해 보존 될 수 있지만 보장되지는 않으며 항상 그런 식으로 작동하지는 않습니다. 그 사이에 몇 가지 제거 작업을 수행하는 것만으로 충분하다는 것을 입증합니다.

는에 대한 자세한 내용은이 질문을 참조하십시오 : Does HashSet preserve insertion order?

나는 간단히 삽입 순서를 보장하는 HashSet을 구현했습니다. 항목을 조회하는 데 Dictionary을 사용하고 순서를 유지하려면 LinkedList을 사용합니다. 3 가지 삽입, 제거 및 검색 작업은 모두 O (1)에서 계속 수행됩니다.

public class OrderedSet<T> : ICollection<T> 
{ 
    private readonly IDictionary<T, LinkedListNode<T>> m_Dictionary; 
    private readonly LinkedList<T> m_LinkedList; 

    public OrderedSet() 
     : this(EqualityComparer<T>.Default) 
    { 
    } 

    public OrderedSet(IEqualityComparer<T> comparer) 
    { 
     m_Dictionary = new Dictionary<T, LinkedListNode<T>>(comparer); 
     m_LinkedList = new LinkedList<T>(); 
    } 

    public int Count 
    { 
     get { return m_Dictionary.Count; } 
    } 

    public virtual bool IsReadOnly 
    { 
     get { return m_Dictionary.IsReadOnly; } 
    } 

    void ICollection<T>.Add(T item) 
    { 
     Add(item); 
    } 

    public bool Add(T item) 
    { 
     if (m_Dictionary.ContainsKey(item)) return false; 
     LinkedListNode<T> node = m_LinkedList.AddLast(item); 
     m_Dictionary.Add(item, node); 
     return true; 
    } 

    public void Clear() 
    { 
     m_LinkedList.Clear(); 
     m_Dictionary.Clear(); 
    } 

    public bool Remove(T item) 
    { 
     LinkedListNode<T> node; 
     bool found = m_Dictionary.TryGetValue(item, out node); 
     if (!found) return false; 
     m_Dictionary.Remove(item); 
     m_LinkedList.Remove(node); 
     return true; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return m_LinkedList.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public bool Contains(T item) 
    { 
     return m_Dictionary.ContainsKey(item); 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     m_LinkedList.CopyTo(array, arrayIndex); 
    } 
} 
+8

OP는'HashSet '는 주문을 보존한다고 말하지 않습니다. 정확히 당신이 누구와 동의하지 않습니까? – user7116

+0

다른 주문에 대해서는 "out of box"라고 명시된 높은 대답이있었습니다. 이제 이들은 낮추어 져 있습니다. 나는 내 텍스트를 수정하고 그것을 중성으로 바꿀 것이다. –

+1

실제로'IEqualityComparer '을 과부하로하여'IDictionary '과 같은 오버로드를 전달해야합니다. 기본적으로 객체 참조 (클래스 별)를 통해 평등을 확인하고 해당 동작을 변경할 수있는 옵션이 제공되지 않습니다. 그러나 나는 이것이 간단한 예라는 것을 이해합니다. –

15

쉽게 TKEY 및 TITEM에 대해 동일한 유형의 인수 지정 KeyedCollection<TKey,TItem>을 사용하여이 기능을 얻을 수 있습니다 : 당신이 Add, Remove, Contains 및 주문 보존 일정 복잡성을해야하는 경우

public class OrderedHashSet<T> : KeyedCollection<T, T> 
{ 
    protected override T GetKeyForItem(T item) 
    { 
     return item; 
    } 
} 
+2

'Remove '를 호출하면 [Remove (T)] (http://msdn.microsoft.com/en-us/library/ms132413(v=vs.110) .aspx) 또는 ['Remove (TKey)'] (http://msdn.microsoft.com/en-us/library/ms132459 (v = vs.110) .aspx)? 첫 번째는 O (n)이고 두 번째는 O (1)입니다. –

+2

가장 파생 된 클래스의 클래스이기 때문에'Remove (TKey) '를 호출합니다. 그러나 콜렉션이 콜렉션 또는 ICollection 으로 캐스트 될 때 Remove()를 호출하면 'Remove (T)'오버로드가 호출됩니다. – kcnygaard

+3

또 다른 설명 :'KeyedCollection '이 목록을 사용하여 항목을 저장하기 때문에'Remove (T)'와'Remove (TKey)'모두 O (n)입니다. – kcnygaard

3

을 한 후 아무 없다 .NET Framework 4.5에서 이러한 컬렉션.

당신이 제 3 자 코드 괜찮아, 내 저장소 (허용 MIT 라이센스)를 살펴 : 고전 HashSet<T> 소스 코드를 기반으로

  • (에서 : https://github.com/OndrejPetrzilka/Rock.Collections

    OrderedHashSet<T> 수집있다 .NET 코어)

  • 보존 삽입 순서수동 재주문
  • 열거
  • 반전
  • 기능 동일한 동작 복잡성HashSet<T> 같은
  • AddRemove 작업 HashSet<T>
  • 는 I 상상
관련 문제