[c#] 중복 키를 허용하는 C # 정렬 가능한 컬렉션

보고서에 다양한 개체가 나타나는 순서를 설정하는 프로그램을 작성 중입니다. 시퀀스는 Excel 스프레드 시트의 Y 위치 (셀)입니다.

코드의 데모 부분은 다음과 같습니다. 내가 달성하고 싶은 것은 컬렉션을 갖는 것입니다. 이렇게하면 여러 개체를 추가 할 수 있고 시퀀스에 따라 정렬 된 컬렉션을 얻을 수 있습니다.

SortedList list = new SortedList();

Header h = new Header();
h.XPos = 1;
h.name = "Header_1";
list.Add(h.XPos, h);

h = new Header();
h.XPos = 1;
h.name = "Header_2";
list.Add(h.XPos, h);

나는 SortedList가 이것을 허용하지 않을 것이라는 것을 알고 있으며 대안을 찾고 있습니다. 중복 을 제거 하고 싶지 않고 이미 시도했습니다 List<KeyValuePair<int, object>>.

감사.



답변

나만의 IComparer를 사용하세요!

다른 답변에서 이미 언급했듯이 자신의 비교 자 클래스를 사용해야합니다. 이를 위해 IComparable을 구현하는 모든 것과 함께 작동하는 일반 IComparer 클래스를 사용합니다.

/// <summary>
/// Comparer for comparing two keys, handling equality as beeing greater
/// Use this Comparer e.g. with SortedLists or SortedDictionaries, that don't allow duplicate keys
/// </summary>
/// <typeparam name="TKey"></typeparam>
public class DuplicateKeyComparer<TKey>
                :
             IComparer<TKey> where TKey : IComparable
{
    #region IComparer<TKey> Members

    public int Compare(TKey x, TKey y)
    {
        int result = x.CompareTo(y);

        if (result == 0)
            return 1;   // Handle equality as beeing greater
        else
            return result;
    }

    #endregion
}

새 SortedList, SortedDictionary 등을 인스턴스화 할 때 사용합니다.

SortedList<int, MyValueClass> slist = new SortedList<int, MyValueClass>(new DuplicateKeyComparer<int>());

여기서 int는 중복 될 수있는 키입니다.


답변

List <>를 안전하게 사용할 수 있습니다. List에는 IComparer를 허용하는 오버로드 인 Sort 메서드가 있습니다. 고유 한 분류기 클래스를. 다음은 예입니다.

private List<Curve> Curves;
this.Curves.Sort(new CurveSorter());

public class CurveSorter : IComparer<Curve>
{
    public int Compare(Curve c1, Curve c2)
    {
        return c2.CreationTime.CompareTo(c1.CreationTime);
    }
}


답변

다음을 사용합니다.

public class TupleList<T1, T2> : List<Tuple<T1, T2>> where T1 : IComparable
{
    public void Add(T1 item, T2 item2)
    {
        Add(new Tuple<T1, T2>(item, item2));
    }

    public new void Sort()
    {
        Comparison<Tuple<T1, T2>> c = (a, b) => a.Item1.CompareTo(b.Item1);
        base.Sort(c);
    }

}

내 테스트 케이스 :

[TestMethod()]
    public void SortTest()
    {
        TupleList<int, string> list = new TupleList<int, string>();
        list.Add(1, "cat");
        list.Add(1, "car");
        list.Add(2, "dog");
        list.Add(2, "door");
        list.Add(3, "elephant");
        list.Add(1, "coconut");
        list.Add(1, "cab");
        list.Sort();
        foreach(Tuple<int, string> tuple in list)
        {
            Console.WriteLine(string.Format("{0}:{1}", tuple.Item1,tuple.Item2));
        }
        int expected_first = 1;
        int expected_last = 3;
        int first = list.First().Item1;  //requires using System.Linq
        int last = list.Last().Item1;    //requires using System.Linq
        Assert.AreEqual(expected_first, first);
        Assert.AreEqual(expected_last, last);
    }

출력 :

1:cab
1:coconut
1:car
1:cat
2:door
2:dog
3:elephant


답변

문제는 데이터 구조 설계가 요구 사항과 일치하지 않는다는 것입니다. 동일한 XPos에 대해 여러 헤더를 저장해야합니다. 따라서 SortedList<XPos, value>의 값이 Header아니라 값을 가져야합니다 List<Header>. 간단하고 사소한 변경이지만 모든 문제를 해결하고 다른 제안 된 솔루션과 같은 새로운 문제를 생성하지 않습니다 (아래 설명 참조).

using System;
using System.Collections.Generic;

namespace TrySortedList {
  class Program {

    class Header {
      public int XPos;
      public string Name;
    }

    static void Main(string[] args) {
      SortedList<int, List<Header>> sortedHeaders = new SortedList<int,List<Header>>();
      add(sortedHeaders, 1, "Header_1");
      add(sortedHeaders, 1, "Header_2");
      add(sortedHeaders, 2, "Header_3");
      foreach (var headersKvp in sortedHeaders) {
        foreach (Header header in headersKvp.Value) {
          Console.WriteLine(header.XPos + ": " + header.Name);
        }
      }
    }

    private static void add(SortedList<int, List<Header>> sortedHeaders, int xPos, string name) {
      List<Header> headers;
      if (!sortedHeaders.TryGetValue(xPos, out headers)){
        headers = new List<Header>();
        sortedHeaders[xPos] = headers;
      }
      headers.Add(new Header { XPos = xPos, Name = name });
    }
  }
}

Output:
1: Header_1
1: Header_2
2: Header_3

임의의 숫자를 추가하거나 동일한 값을 가진 2 개의 XPos가 다른 것처럼 가장하는 것과 같은 “재미있는”키를 추가하면 다른 많은 문제가 발생합니다. 예를 들어 특정 헤더를 제거하는 것이 어렵거나 불가능 해집니다.

또한 List<Header>모든 .NET보다 소수만 정렬해야하는 경우 정렬 성능이 훨씬 더 좋습니다 Header. 예 : XPos가 100 개이고 각각 ​​헤더가 100 개인 경우 100이 아니라 10000 Header을 정렬해야합니다.List<Header> .

물론이 솔루션에도 단점이 있습니다. 헤더가 1 개만있는 XPos가 많으면 목록을 많이 만들어야하므로 약간의 오버 헤드가 발생합니다.


답변

가장 간단한 솔루션 (위의 모든 것과 비교) :을 사용 SortedSet<T>하고 IComparer<SortableKey>클래스를 허용 한 다음 다음과 같이 Compare 메서드를 구현합니다.

public int Compare(SomeClass x, SomeClass y)
{
    var compared = x.SomeSortableKeyTypeField.CompareTo(y.SomeSortableKeyTypeField);
    if (compared != 0)
        return compared;

    // to allow duplicates
    var hashCodeCompare = x.GetHashCode().CompareTo(y.GetHashCode());
    if (hashCodeCompare != 0)
        return hashCodeCompare;

    if (Object.ReferenceEquals(x, y))
        return 0;

    // for weird duplicate hashcode cases, throw as below or implement your last chance comparer
    throw new ComparisonFailureException();

}


답변

도와 주셔서 정말로 고맙습니다. 더 많이 검색하는 동안이 해결책을 찾았습니다. (다른 질문은 Stackoverflow.com에서 사용 가능)

먼저 클래스 (Headers, Footer 등)에 대한 개체를 캡슐화하는 클래스를 만들었습니다.

public class MyPosition
{
    public int Position { get; set; }
    public object MyObjects{ get; set; }
}

따라서이 클래스는 객체를 보유해야하며 각 객체의 PosX는 int Position으로 이동합니다.

List<MyPosition> Sequence= new List<MyPosition>();
Sequence.Add(new MyPosition() { Position = 1, Headerobject });
Sequence.Add(new MyPosition() { Position = 2, Headerobject1 });
Sequence.Add(new MyPosition() { Position = 1, Footer });

League.Sort((PosA, PosB) => PosA.Position.CompareTo(PosB.Position));

결국 내가 얻는 것은 정렬 된 “시퀀스”목록입니다.


답변

Lookup<TKey, TElement>중복 키를 허용
하려고 시도 했습니까 http://msdn.microsoft.com/en-us/library/bb460184.aspx