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


94

보고서에 다양한 개체가 나타나는 순서를 설정하는 프로그램을 작성 중입니다. 시퀀스는 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>>.

감사.


1
컬렉션 이 초기 구성원 목록을받은 삽입 / 제거를 지원해야합니까 ?
Ani

2
시도했을 때 작동하지 않은 것은 무엇입니까 List?
diceguyd30

나는 단지 정렬하고 객체를 얻고 싶지 않습니다. 그러나 오히려 전체 정렬 목록을 얻고 싶습니다. 따라서 아래 예에서 두 Header 개체가 모두 존재해야하며 순서대로 다른 개체 아래에 있어야합니다. XPos = 2를 사용하여 다른 Header 개체를 추가하면 목록에 3 개 개체, XPos = 1 인 개체 2 개, XPos = 2로 세 번째 개체가 있어야합니다.
Mayur Kotlikar 2011

참고 사항 : 이러한 유형의 상황이 발생하면 찾을 수 없는 항목에 대해 잘 알려지지 않은 BinarySearch 동작 과 결합 된 일반 목록이 경이로움을 발견 했습니다.
J Trana 2014

답변:


76

나만의 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는 중복 될 수있는 키입니다.


40
그러나 여기에서 키를 제거 할 수 없습니다.
Shashwat 2014-06-18

11
네 맞습니다, Shachwat! 비교자가 키 동등성을 알리기 위해 0을 반환하지 않기 때문에 Remove (key) 또는 IndexOfKey (key)를 사용할 수 없습니다. 그러나 인덱스가있는 경우 RemoveAt (index)하여 항목을 삭제할 수 있습니다.
Knasterbax 2014-06-23

1
나는 또한 같은 문제가 발생하여 SortedDictionary. 제거도 가능합니다.
Shashwat 2014-06-24

10
이런 식으로 비교 자의 반사성 을 깨고 있다는 점에 유의 하십시오. 그것은 BCL에서 일을 깰 수 있습니다.
ghord

1
주문을 유지하려면 실제로 -1을 반환해야합니다.
M.kazem Akhgary

16

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);
    }
}

1
나는 단지 정렬하고 객체를 얻고 싶지 않습니다. 그러나 오히려 전체 정렬 목록을 얻고 싶습니다. 따라서 아래 예에서 두 Header 개체가 모두 존재해야하며 순서대로 다른 개체 아래에 있어야합니다. XPos = 2를 사용하여 다른 Header 개체를 추가하면 목록에 3 개 개체, XPos = 1 인 개체 2 개, XPos = 2로 세 번째 개체가 있어야합니다
Mayur Kotlikar 2011

1
좋습니다. 요소가 목록에 삽입 될 때 정렬에 따라 올바른 위치에 삽입되어야합니다. 틀렸다면 정정 해주세요. 한 번 살펴 보겠습니다. 잠시 후에 돌아올 것입니다.
Dipti Mehta 2011

List <T> .Sort는 컬렉션 크기에 따라 여러 정렬 알고리즘을 사용하며 모두 안정적인 정렬은 아닙니다. 따라서 등가물과 비교되는 컬렉션에 추가 된 개체는 추가 된 순서대로 나타나지 않을 수 있습니다.
조용한 톤

내가 사전에 기능을 감소 적용으로 KeyValuePairs의 과도한 금액을 만드는 막을 수 있도록 내가이 옵션으로 갔다
크리스 Marisic을

10

다음을 사용합니다.

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

튜플은 .NET의 모든 버전에서 사용할 수 없지만 KeyValuePair <K, V>로 각각 대체 될 수있다
르우벤

6

문제는 데이터 구조 설계가 요구 사항과 일치하지 않는다는 것입니다. 동일한 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가 많으면 목록을 많이 만들어야하므로 약간의 오버 헤드가 발생합니다.


이것은 가장 간단한 솔루션입니다. 또한 SortedDictionary를 확인하십시오. 어떤 경우에는 비슷하고 빠릅니다.
Hogan

이것은 정말 좋은 해결책입니다. 이 기능을 사용자 지정 컬렉션 개체에 쉽게 래핑 할 수 있으며 매우 유용합니다. 좋은 생각, Peter를 공유해 주셔서 감사합니다!
Adam P

5

가장 간단한 솔루션 (위의 모든 것과 비교) :을 사용 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();

}

4
나는 SortedSet <T>를 사용했고 T는 다른 Field (s)가 동일하더라도 각 T가 고유한지 확인하기 위해 각 인스턴스화에서 증가하는 자체 증가하는 int ID를 가졌습니다.
Skychan 2017-04-19

3
비교를위한 GetHashCode는 위험합니다. 예기치 않은 거짓 중복이 발생할 수 있습니다. 대부분의 경우 작동 할 수 있지만 심각한 경우에는 사용하지 않습니다.
Hogan

4

도와 주셔서 정말로 고맙습니다. 더 많이 검색하는 동안이 해결책을 찾았습니다. (다른 질문은 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));

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


2

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


감사. 내 문제는 개체가 한 가지 유형 (머리글이 아님)이 아니라 다양 할 수 있지만 (바닥 글, 사이드 바 등) 각 개체에는 XPos가있을 것입니다
Mayur Kotlikar 2011

또한 Lookup내가 믿는 공개 생성자가 없습니다 . 이 문제를 해결하는 좋은 방법이 있습니까?
Jeff B

1
@JeffBridgman 당신은 Linq에 의존해야 할 것입니다. 당신은 할 수 ToLookup있는에 IEnumerable<T>.
nawfal 2013 년

7
예, 중복 키를 허용하지만 정렬 된 항목은 유지하지 않습니다!
Roman Starkov 2014 년

2

SortedList를 사용하고 TKey에 값을 사용하고 TValue에 int (수)를 사용할 수 있습니다.

다음은 샘플입니다. 단어의 문자를 정렬하는 함수.

    private string sortLetters(string word)
    {
        var input = new System.Collections.Generic.SortedList<char, int>();

        foreach (var c in word.ToCharArray())
        {
            if (input.ContainsKey(c))
                input[c]++;
            else
                input.Add(c, 1);
        }

        var output = new StringBuilder();

        foreach (var kvp in input)
        {
            output.Append(kvp.Key, kvp.Value);
        }

        string s;

        return output.ToString();

    }

2

이 컬렉션 클래스는 중복을 유지하고 중복에 대한 정렬 순서를 삽입합니다. 트릭은 항목이 삽입 될 때 고유 한 값으로 태그를 지정하여 안정적인 정렬 순서를 유지하는 것입니다. 그런 다음 모든 것을 ICollection 인터페이스로 래핑합니다.

public class SuperSortedSet<TValue> : ICollection<TValue>
{
    private readonly SortedSet<Indexed<TValue>> _Container;
    private int _Index = 0;
    private IComparer<TValue> _Comparer;

    public SuperSortedSet(IComparer<TValue> comparer)
    {
        _Comparer = comparer;
        var c2 = new System.Linq.Comparer<Indexed<TValue>>((p0, p1) =>
        {
            var r = _Comparer.Compare(p0.Value, p1.Value);
            if (r == 0)
            {
                if (p0.Index == -1
                    || p1.Index == -1)
                    return 0;

                return p0.Index.CompareTo(p1.Index);

            }
            else return r;
        });
        _Container = new SortedSet<Indexed<TValue>>(c2);
    } 

    public IEnumerator<TValue> GetEnumerator() { return _Container.Select(p => p.Value).GetEnumerator(); }

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

    public void Add(TValue item) { _Container.Add(Indexed.Create(_Index++, item)); }

    public void Clear() { _Container.Clear();}

    public bool Contains(TValue item) { return _Container.Contains(Indexed.Create(-1,item)); }

    public void CopyTo(TValue[] array, int arrayIndex)
    {
        foreach (var value in this)
        {
            if (arrayIndex >= array.Length)
            {
                throw new ArgumentException("Not enough space in array");
            }
            array[arrayIndex] = value;
            arrayIndex++;
        }
    }

    public bool Remove(TValue item) { return _Container.Remove(Indexed.Create(-1, item)); }

    public int Count {
        get { return _Container.Count; }
    }
    public bool IsReadOnly {
        get { return false; }
    }
}

시험 수업

[Fact]
public void ShouldWorkWithSuperSortedSet()
{
    // Sort points according to X
    var set = new SuperSortedSet<Point2D>
        (new System.Linq.Comparer<Point2D>((p0, p1) => p0.X.CompareTo(p1.X)));

    set.Add(new Point2D(9,10));
    set.Add(new Point2D(1,25));
    set.Add(new Point2D(11,-10));
    set.Add(new Point2D(2,99));
    set.Add(new Point2D(5,55));
    set.Add(new Point2D(5,23));
    set.Add(new Point2D(11,11));
    set.Add(new Point2D(21,12));
    set.Add(new Point2D(-1,76));
    set.Add(new Point2D(16,21));

    var xs = set.Select(p=>p.X).ToList();
    xs.Should().BeInAscendingOrder();
    xs.Count.Should()
       .Be(10);
    xs.ShouldBeEquivalentTo(new[]{-1,1,2,5,5,9,11,11,16,21});

    set.Remove(new Point2D(5,55));
    xs = set.Select(p=>p.X).ToList();
    xs.Count.Should()
       .Be(9);
    xs.ShouldBeEquivalentTo(new[]{-1,1,2,5,9,11,11,16,21});

    set.Remove(new Point2D(5,23));
    xs = set.Select(p=>p.X).ToList();
    xs.Count.Should()
       .Be(8);
    xs.ShouldBeEquivalentTo(new[]{-1,1,2,9,11,11,16,21});

    set.Contains(new Point2D(11, 11))
       .Should()
       .BeTrue();

    set.Contains(new Point2D(-1, 76))
        .Should().BeTrue();

    // Note that the custom compartor function ignores the Y value
    set.Contains(new Point2D(-1, 66))
        .Should().BeTrue();

    set.Contains(new Point2D(27, 66))
        .Should().BeFalse();

}

태깅 구조체

public struct Indexed<T>
{
    public int Index { get; private set; }
    public T Value { get; private set; }
    public Indexed(int index, T value) : this()
    {
        Index = index;
        Value = value;
    }

    public override string ToString()
    {
        return "(Indexed: " + Index + ", " + Value.ToString () + " )";
    }
}

public class Indexed
{
    public static Indexed<T> Create<T>(int indexed, T value)
    {
        return new Indexed<T>(indexed, value);
    }
}

람다 비교 자 도우미

public class Comparer<T> : IComparer<T>
{
    private readonly Func<T, T, int> _comparer;

    public Comparer(Func<T, T, int> comparer)
    {
        if (comparer == null)
            throw new ArgumentNullException("comparer");
        _comparer = comparer;
    }

    public int Compare(T x, T y)
    {
        return _comparer(x, y);
    }
}

1

문제는 키가 아닌 것을 키로 사용한다는 것입니다 (여러 번 발생하기 때문입니다).

따라서 실제 좌표가있는 경우 PointSortedList의 키로 가져와야합니다.

또는 List<List<Header>>첫 번째 목록 색인이 x 위치를 정의하고 내부 목록 색인이 y 위치를 정의 하는 위치 를 만듭니다 (또는 원하는 경우 그 반대로).


키는 기본 키가 아닌 한 여러 인스턴스를 가질 수 있습니다. 적어도 그것이 내가 들었던 데이터베이스 수업에서 그들이 나에게 말한 것입니다.
융합

1
이 답변은 약간 짧지 만 문제를 제대로 설명하고 올바른 솔루션을 제공합니다 (예 : SortedList <int, List <Header >> 사용). 이것은 정렬 된 헤더를 유지하고 동일한 xPos에 많은 헤더를 저장할 수 있습니다. 코드 샘플을 보려면 내 대답을 찾으십시오. 나는 올바른 방향을 가리 키기 때문에이 대답을 하나 더했습니다. 도움이된다고 생각되면 내 답변을 1 개 더해주세요.
Peter Huber

1

이것의 핵심 (의도 된 말장난)은 IComparable동일성과 해싱을 유지하지만 동일하지 않으면 0과 비교하지 않는 기반 클래스 를 만드는 것 입니다. 이 작업을 수행 할 수 있으며 몇 가지 보너스로 만들 수 있습니다. 안정적인 정렬 (즉, 정렬 된 목록에 먼저 추가 된 값이 위치를 유지함)하고 ToString()단순히 실제 키 문자열 값을 반환 할 수 있습니다.

트릭을 수행해야하는 구조체 키는 다음과 같습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace System
{
    /// <summary>
    /// Defined in Totlsoft.Util.
    /// A key that will always be unique but compares
    /// primarily on the Key property, which is not required
    /// to be unique.
    /// </summary>
    public struct StableKey : IComparable<StableKey>, IComparable
    {
        private static long s_Next;
        private long m_Sequence;
        private IComparable m_Key;

        /// <summary>
        /// Defined in Totlsoft.Util.
        /// Constructs a StableKey with the given IComparable key.
        /// </summary>
        /// <param name="key"></param>
        public StableKey( IComparable key )
        {
            if( null == key )
                throw new ArgumentNullException( "key" );

            m_Sequence = Interlocked.Increment( ref s_Next );
            m_Key = key;
        }

        /// <summary>
        /// Overridden. True only if internal sequence and the
        /// Key are equal.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals( object obj )
        {
            if( !( obj is StableKey ) )
                return false;

            var dk = (StableKey)obj;

            return m_Sequence.Equals( dk.m_Sequence ) &&
                Key.Equals( dk.Key );
        }

        /// <summary>
        /// Overridden. Gets the hash code of the internal
        /// sequence and the Key.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return m_Sequence.GetHashCode() ^ Key.GetHashCode();
        }

        /// <summary>
        /// Overridden. Returns Key.ToString().
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Key.ToString();
        }

        /// <summary>
        /// The key that will be compared on.
        /// </summary>
        public IComparable Key
        {
            get
            {
                if( null == m_Key )
                    return 0;

                return m_Key;
            }
        }

        #region IComparable<StableKey> Members

        /// <summary>
        /// Compares this Key property to another. If they
        /// are the same, compares the incremented value.
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo( StableKey other )
        {
            var cmp = Key.CompareTo( other.Key );
            if( cmp == 0 )
                cmp = m_Sequence.CompareTo( other.m_Sequence );

            return cmp;
        }

        #endregion

        #region IComparable Members

        int IComparable.CompareTo( object obj )
        {
            return CompareTo( (StableKey)obj );
        }

        #endregion
    }
}

좋은 생각입니다. 개념을 사용자 지정 ICollection으로 래핑했습니다. stackoverflow.com/a/21625939/158285
bradgonesurfing 2014

0

Linq.Lookup 은 멋지지만 "키"를 복제하는 동안 단순히 반복하는 것이 목표라면 다음 구조를 사용할 수 있습니다.

List<KeyValuePair<String, String>> FieldPatterns = new List<KeyValuePair<string, string>>() {
   new KeyValuePair<String,String>("Address","CommonString"),
   new KeyValuePair<String,String>("Username","UsernamePattern"),
   new KeyValuePair<String,String>("Username","CommonString"),
};

그런 다음 다음과 같이 작성할 수 있습니다.

foreach (KeyValuePair<String,String> item in FieldPatterns)
{
   //use item.Key and item.Value
}

HTH


0

비결은 고유 키로 개체를 확장하는 것입니다. 통과하는 다음 테스트를 참조하십시오. 내 포인트를 X 값으로 정렬하고 싶습니다. 내 비교 함수에서 벌거 벗은 Point2D를 사용하면 동일한 X 값을 가진 포인트가 제거됩니다. 그래서 Indexed라는 태그 지정 클래스에 Point2D를 래핑합니다.

[Fact]
public void ShouldBeAbleToUseCustomComparatorWithSortedSet()
{
    // Create comparer that compares on X value but when X
    // X values are uses the index
    var comparer = new 
        System.Linq.Comparer<Indexed<Point2D>>(( p0, p1 ) =>
        {
            var r = p0.Value.X.CompareTo(p1.Value.X);
            return r == 0 ? p0.Index.CompareTo(p1.Index) : r;
        });

    // Sort points according to X
    var set = new SortedSet<Indexed<Point2D>>(comparer);

    int i=0;

    // Create a helper function to wrap each point in a unique index
    Action<Point2D> index = p =>
    {
        var ip = Indexed.Create(i++, p);
        set.Add(ip);
    };

    index(new Point2D(9,10));
    index(new Point2D(1,25));
    index(new Point2D(11,-10));
    index(new Point2D(2,99));
    index(new Point2D(5,55));
    index(new Point2D(5,23));
    index(new Point2D(11,11));
    index(new Point2D(21,12));
    index(new Point2D(-1,76));
    index(new Point2D(16,21));
    set.Count.Should()
       .Be(10);
    var xs = set.Select(p=>p.Value.X).ToList();
    xs.Should()
      .BeInAscendingOrder();
    xs.ShouldBeEquivalentTo(new[]{-1,1,2,5,5,9,11,11,16,21});

}

이 작업을 수행하는 유틸리티는

람다를 취하는 비교 자

public class Comparer<T> : IComparer<T>
{
    private readonly Func<T, T, int> _comparer;

    public Comparer(Func<T, T, int> comparer)
    {
        if (comparer == null)
            throw new ArgumentNullException("comparer");
        _comparer = comparer;
    }

    public int Compare(T x, T y)
    {
        return _comparer(x, y);
    }
}

태그 지정 구조체

public struct Indexed<T>
{
    public int Index { get; private set; }
    public T Value { get; private set; }
    public Indexed(int index, T value) : this()
    {
        Index = index;
        Value = value;
    }

    public override string ToString()
    {
        return "(Indexed: " + Index + ", " + Value.ToString () + " )";
    }
}

public class Indexed
{
    public static Indexed<T> Create<T>(int indexed, T value)
    {
        return new Indexed<T>(indexed, value);
    }
}

위의 개념을 사용자 지정 ICollection 클래스로 완전히 래핑하려면 다른 답변을 참조하십시오
bradgonesurfing

0

이것이 제가 문제를 해결 한 방법입니다. lock필요하지 않은 경우 간단히 s 를 제거 할 수 있지만 스레드로부터 안전 합니다. 또한 임의 Insert의 인덱스는 정렬 조건을 위반할 수 있으므로 지원되지 않습니다.

public class ConcurrentOrderedList<Titem, Tsort> : ICollection<Titem>
{
    private object _lock = new object();
    private SortedDictionary<Tsort, List<Titem>> _internalLists;
    Func<Titem, Tsort> _getSortValue;
    
    public ConcurrentOrderedList(Func<Titem,Tsort> getSortValue)
    {
        _getSortValue = getSortValue;
        _internalLists = new SortedDictionary<Tsort, List<Titem>>();            
    }

    public int Count { get; private set; }

    public bool IsReadOnly => false;

    public void Add(Titem item)
    {
        lock (_lock)
        {
            List<Titem> values;
            Tsort sortVal = _getSortValue(item);
            if (!_internalLists.TryGetValue(sortVal, out values))
            {
                values = new List<Titem>();
                _internalLists.Add(sortVal, values);
            }
            values.Add(item);
            Count++;
        }            
    }

    public bool Remove(Titem item)
    {
        lock (_lock)
        {
            List<Titem> values;
            Tsort sortVal = _getSortValue(item);
            if (!_internalLists.TryGetValue(sortVal, out values))
                return false;

            var removed = values.Remove(item);
            if (removed)
                Count--;
            return removed;
        }
    }

    public void Clear()
    {
        lock (_lock)
        {
            _internalLists.Clear();
        }
    }

    public bool Contains(Titem item)
    {
        lock (_lock)
        {
            List<Titem> values;
            Tsort sortVal = _getSortValue(item);
            if (!_internalLists.TryGetValue(sortVal, out values))
                return false;
            return values.Contains(item);
        }
    }

    public void CopyTo(Titem[] array, int arrayIndex)
    {
        int i = arrayIndex;
        lock (_lock)
        {
            foreach (var list in _internalLists.Values)
            {
                list.CopyTo(array, i);
                i += list.Count;
            }
        }
    }

    public IEnumerator<Titem> GetEnumerator()
    {
        foreach (var list in _internalLists.Values)
        {
            foreach (var item in list)
                yield return item;
        }
    }

    public int IndexOf(Titem item)
    {
        int i = 0;
        var sortVal = _getSortValue(item);
        lock (_lock)
        {               
            foreach (var list in _internalLists)
            {
                if (object.Equals(list.Key, sortVal))
                {
                    int intIndex = list.Value.IndexOf(item);
                    if (intIndex == -1)
                        return -1;
                    return i + intIndex;
                }
                i += list.Value.Count;
            }
            return -1;
        }           
    }

    public void Insert(int index, Titem item)
    {
        throw new NotSupportedException();
    }

    // Note this method is indeterminate if there are multiple
    // items in the same sort position!
    public void RemoveAt(int index)
    {
        int i = 0;
        lock (_lock)
        {
            foreach (var list in _internalLists.Values)
            {
                if (i + list.Count < index)
                {
                    i += list.Count;
                    continue;
                }
                else
                {
                    list.RemoveAt(index - i);
                    return;
                }
            }
        }
    }

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

-1

클래스를 만들고 목록을 쿼리합니다.

Public Class SortingAlgorithm
{
    public int ID {get; set;}
    public string name {get; set;}
    public string address1 {get; set;}
    public string city {get; set;}
    public string state {get; set;}
    public int age {get; set;}
}

//declare a sorting algorithm list
List<SortingAlgorithm> sortAlg = new List<SortingAlgorithm>();

//Add multiple values to the list
sortAlg.Add( new SortingAlgorithm() {ID = ID, name = name, address1 = address1, city = city, state = state, age = age});
sortAlg.Add( new SortingAlgorithm() {ID = ID, name = name, address1 = address1, city = city, state = state, age = age});
sortAlg.Add( new SortingAlgorithm() {ID = ID, name = name, address1 = address1, city = city, state = state, age = age});

//query and order by the list
  var sortedlist = (from s in sortAlg
                    select new { s.ID, s.name, s.address1, s.city, s.state, s.age })
                                                     .OrderBy(r => r.ID)
                                                     .ThenBy(r=> r.name)
                                                     .ThenBy(r=> r.city)
                                                     .ThenBy(r=>r.state)
                                                     .ThenBy(r=>r.age);

-1

여기에 내 의견이 있습니다. 여기에 드래곤이있을 수 있습니다. C #은 여전히 ​​저에게 아주 새로운 것입니다.

  • 중복 키가 허용되고 값은 목록에 저장됩니다.
  • 정렬 된 큐로 사용 했으므로 이름과 방법

용법:

SortedQueue<MyClass> queue = new SortedQueue<MyClass>();
// new list on key "0" is created and item added
queue.Enqueue(0, first);
// new list on key "1" is created and item added
queue.Enqueue(1, second);
// items is added into list on key "0"
queue.Enqueue(0, third);
// takes the first item from list with smallest key
MyClass myClass = queue.Dequeue();
class SortedQueue<T> {
  public int Count;
  public SortedList<int, List<T>> Queue;

  public SortedQueue() {
    Count = 0;
    Queue = new SortedList<int, List<T>>();
  }

  public void Enqueue(int key, T value) {
    List<T> values;
    if (!Queue.TryGetValue(key, out values)){
      values = new List<T>();
      Queue.Add(key, values);
      Count += 1;
    }
    values.Add(value);
  }

  public T Dequeue() {
    if (Queue.Count > 0) {
      List<T> smallest = Queue.Values[0];
      if (smallest.Count > 0) {
        T item = smallest[0];
        smallest.Remove(item);
        return item;
      } else {
        Queue.RemoveAt(0);
        Count -= 1;
        return Dequeue();
      }
    }
    return default(T);
  }
}

QueueBCL 에는 이미 선입 선출 항목 컬렉션을 나타내는 클래스 가 있습니다. 클래스의 의미가 다릅니다. 수업에는 시작 (항목이 대기열에서 제외됨)이 있지만 끝은 없습니다 (항목은 어디에나 삽입 할 수 있음). 따라서 Enqueue수업 의 방법은 무의미한 IMHO입니다.
Theodor Zoulias

@TheodorZoulias Yup, 이름 지정은 여기에서 약간 쉬 * t하지만 나는 그것이 투표 할 가치가 있다고 생각하지 않습니다 .OP에 필요한 것이 있으며 입 / 출력 방법의 이름을 바꾸고 다시 구현하는 문제 일뿐입니다. 왜 이렇게 부르나요? while 루프에서 처음부터 비울 수 있고 우선 순위 값에 따라 새 항목을 추가 할 수있는 구조가 필요했습니다. 그래서 PriorityQueue더 적절한 이름이 될 것입니다.
솔로

OP는 중복 키를 허용하는 정렬 가능한 컬렉션을 원합니다. 클래스는 열거 할 수 없으므로 컬렉션 이 아닙니다 . 나는 또한 공공 장소의 사용을 싫어한다. 개인적으로 반대표를받지 마십시오. 한 번의 업 보트 ( -2 * 5 == +10) 로 5 번의 다운 보트의 평판 피해를 복구 할 수 있으므로 큰 문제가 아닙니다. :-)
Theodor Zoulias
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.