C # Сортована колекція, яка дозволяє повторювати ключі


94

Я пишу програму для встановлення послідовності, у якій у звіті з’являться різні об’єкти. Послідовність - це Y-позиція (комірка) на електронній таблиці Excel.

Демонстраційна частина коду наведена нижче. Що я хочу досягти - це мати колекцію, яка дозволить мені додати декілька об'єктів, і я можу отримати відсортовану колекцію на основі послідовності

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
Чи потрібно колекції підтримувати вставки / видалення після того, як їй надано початковий список учасників?
Ані

2
Що не спрацювало, коли ви спробували List?
diceguyd30

Я не хочу просто сортувати та отримати об’єкт. Але швидше хочу отримати весь відсортований список. Так, у прикладі нижче, обидва об'єкти Header повинні існувати і послідовно один під одним. Якщо я додаю ще один об’єкт заголовка з XPos = 2, я повинен мати 3 об’єкти у списку, 2 об’єкти з XPos = 1 і третій як XPos = 2
Mayur Kotlikar

Лише зауваження: коли я стикаюся з подібним типом ситуації, я виявляю, що загальний Список у поєднанні з маловідомою поведінкою BinarySearch для не знайдених елементів творить чудеса.
J Trana

Відповіді:


76

Використовуйте власний IComparer!

Як уже було сказано в деяких інших відповідях, ви повинні використовувати власний порівняльний клас. Для цього я використовую загальний клас IComparer, який працює з усім, що реалізує IComparable:

/// <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

11
Так, Шачват! Ви не можете використовувати Remove (ключ) або IndexOfKey (ключ), оскільки порівняльник ніколи не повертає 0 для сигналу рівності ключа. Але ви можете RemoveAt (індекс) видалити елементи, якщо у вас є їх індекс.
Knasterbax

1
Я також зіткнувся з тією ж проблемою, яку я використав SortedDictionary. Це дозволяє також видалити.
Шашват

10
Зауважте, що ви таким чином порушуєте рефлексивність свого порівняльника. Це може (і буде) ламати речі в БКЛ.
ghord

1
це фактично повинно повернутися -1 для наведення порядку
М.казем Ахгарі

16

Ви можете сміливо використовувати Список <>. У списку є метод сортування, перевантаження якого приймає IComparer. Ви можете створити власний клас сортування як. Ось приклад:

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, тоді у мене повинен бути 3 об’єкти у списку, 2 об’єкти з XPos = 1 і третій як XPos = 2
Mayur Kotlikar

1
все гаразд, ви хочете сказати, що щоразу, коли елемент буде вставлений у список, він повинен бути вставлений у правильне положення відповідно до сортування. Будь ласка, виправте мене, якщо не так. Дозвольте мені подивитися, повернуся за короткий момент
Dipti Mehta

Зауважте, що List <T> .Sort використовує кілька алгоритмів сортування, залежно від розміру колекції, і не всі вони є стабільними сортами. Тож об’єкти, додані до колекції, що порівнюють з еквівалентом, можуть не відображатися в тому порядку, коли вони були додані.
тихим тоном

я пішов з цим варіантом, щоб я міг перестати створювати непомірні кількості KeyValuePairs із застосуванням функцій зменшення до словника
Кріс Марісіч,

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

Tuple недоступний у всіх випусках .NET, але його можна замінити KeyValuePair <K, V>
Reuben

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>відсортувати потрібно лише кілька Header. Приклад: Якщо є 100 XPos і кожен має 100 заголовків, 10000 Headerпотрібно сортувати на відміну від 100 List<Header>.

Звичайно, у цього рішення є і недолік: Якщо є багато XPos з лише 1 заголовком, потрібно створити стільки списків, що є деякими накладними витратами.


Це найпростіше рішення. Також перевірте SortedDictionary, він схожий, в деяких випадках швидше.
Хоган

Це дійсно хороше рішення. Можна досить легко зафіксувати цю функціональність у якомусь власному об'єкті колекції, і це було б дуже корисно. Гарна думка, дякую за те, що поділився Петром!
Адам П

5

Найпростіше рішення (порівняно з усім вищезазначеним): використовуйте SortedSet<T>, він приймає IComparer<SortableKey>клас, а потім реалізуйте метод Порівняння таким чином:

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 мав свій власний приріст ідентифікатора int, який збільшується на кожну інстанцію, щоб гарантувати, що кожен T унікальний, навіть якщо інші поля (поля) однакові.
Skychan

3
GetHashCode для порівняння небезпечний. Це може призвести до несподіваного помилкового дубліката. Це може працювати більшу частину часу, але я ніколи не використовував би це для чогось серйозного.
Хоган

4

Велике спасибі за вашу допомогу. Шукаючи більше, я знайшов це рішення. (Доступно в Stackoverflow.com в іншому запитанні)

По-перше, я створив клас, який би інкапсулював мої об'єкти для класів (заголовки, колонтитули тощо)

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


Дякую. Моя проблема полягає в тому, що об’єкти будуть не лише одного типу (тільки не заголовка), вони можуть змінюватися (скажімо Footer, Sidebar тощо), але кожен матиме XPos
Mayur Kotlikar

Крім того, немає громадського конструктора на, Lookupя вважаю. Будь-який хороший шлях для цього?
Jeff B

1
@JeffBridgman вам доведеться покладатися на Linq. Ви можете робити ToLookupна будь-якому IEnumerable<T>.
nawfal

7
Так, це дозволяє повторювати ключі, але нічого не сортує!
Роман Старков

2

Ви можете використовувати SortedList, використовувати ваше значення для TKey та int (count) для TValue.

Ось зразок: Функція, яка сортує літери слова.

    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

Проблема полягає в тому, що ви використовуєте щось як ключ, який не є ключем (тому що це відбувається кілька разів).

Тож якщо у вас є реальні координати, можливо, ви повинні взяти Point ключ як ключ до свого сортованого списку.

Або ви створюєте List<List<Header>>де індекс першого списку визначає позицію x, а внутрішній список індексує y-позицію (або навпаки, якщо вам подобається).


Ключ може мати кілька примірників, якщо це не первинний ключ. Принаймні, саме так мені сказали в класі «Бази даних», який я взяв.
об'єднати

1
Ця відповідь трохи коротка, але вона пояснює проблему належним чином і забезпечує правильне рішення, тобто використовуючи SortedList <int, List <Header>>. Це підтримує впорядкований заголовок і може зберігати багато заголовків в одному xPos. Для зразка коду шукайте мою відповідь. Я відповів однією цією відповіддю, оскільки вона вказує в правильному напрямку. Будь ласка, також 1 моя відповідь, якщо ви вважаєте, що це корисно.
Пітер Хубер

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

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. Тому я загортаю Point2D у клас тегів під назвою Indexed.

[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

Ось як я вирішив проблему. Він призначений для безпечного потоку, хоча ви можете просто видалити locks, якщо вам це не потрібно. Також зауважте, що довільне 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.
Теодор Зуліяс

@TheodorZoulias Yup, називання тут трохи sh * t тут, але я навряд чи думаю, що це заслуговує на зворотний зв'язок, він має те, що потребує ОП, і це лише питання перейменування та перевтілення методів введення / виведення. Чому це називається так? Мені була потрібна структура, яку я можу спочатку очистити від циклу та додати нові елементи на основі значення пріоритету. Так PriorityQueueбуло б більш доречним ім'я.
Соло

ОП хоче сортувати колекцію, яка дозволяє повторювати ключі. Ваш клас не є колекцією , оскільки його неможливо перерахувати. Я також не люблю використання публічних полів. Не сприймайте погляди особисто. Ви можете виправити пошкодження репутації 5 поворотних каналів за допомогою одного upvote ( -2 * 5 == +10), так що це не велика справа. :-)
Теодор Зуліас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.