Raccolta ordinabile C # che consente chiavi duplicate


94

Sto scrivendo un programma per impostare una sequenza in cui vari oggetti appariranno nel report. La sequenza è la posizione Y (cella) sul foglio di calcolo Excel.

Di seguito è riportata una parte demo del codice. Quello che voglio ottenere è avere una raccolta, che mi permetterà di aggiungere più oggetti e posso ottenere una raccolta ordinata in base alla sequenza

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

So che SortedList non lo consentirà e ho cercato alternative. Non voglio eliminare i duplicati e già provato List<KeyValuePair<int, object>>.

Grazie.


1
La raccolta deve supportare inserimenti / rimozioni dopo aver ricevuto l'elenco iniziale dei membri?
Ani

2
Cosa non ha funzionato quando hai provato List?
diceguyd30

Non voglio solo ordinare e ottenere l'oggetto. Ma piuttosto voglio ottenere l'intero elenco ordinato. Quindi, nell'esempio seguente, entrambi gli oggetti Header dovrebbero esistere e in sequenza uno sotto l'altro. Se aggiungo un altro oggetto Header con XPos = 2, dovrei quindi avere 3 oggetti nell'elenco, 2 oggetti con XPos = 1 e terzo come XPos = 2
Mayur Kotlikar

Solo una nota: quando incontro questo tipo di situazione trovo che l'elenco generico in combinazione con il comportamento poco conosciuto di BinarySearch per gli elementi non trovati fa miracoli.
J Trana

Risposte:


76

Usa il tuo IComparer!

Come già affermato in altre risposte, dovresti usare la tua classe di comparatore. Per questo utilizzo una classe IComparer generica, che funziona con tutto ciò che implementa 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
}

Lo userai quando installi un nuovo SortedList, SortedDictionary ecc:

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

Qui int è la chiave che può essere duplicata.


40
Ma non sarai in grado di rimuovere alcuna chiave da esso.
Shashwat

11
Sì, è vero, Shachwat! Non è possibile utilizzare Remove (key) o IndexOfKey (key), perché l'operatore di confronto non restituisce mai 0 per segnalare l'uguaglianza della chiave. Ma potresti RemoveAt (index) per eliminare gli elementi se hai il loro index.
Knasterbax

1
Ho anche riscontrato lo stesso problema, ho usato SortedDictionary. Permette anche la rimozione.
Shashwat

10
Nota che stai rompendo la riflessività del tuo comparatore in questo modo. Può (e romperà) le cose in BCL.
ghord

1
questo dovrebbe effettivamente restituire -1 per mantenere l'ordine
M.kazem Akhgary

16

Puoi tranquillamente usare List <>. List ha un metodo Sort, un overload del quale accetta IComparer. Puoi creare la tua classe selezionatrice come. Ecco un esempio:

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
Non voglio solo ordinare e ottenere l'oggetto. Ma piuttosto voglio ottenere l'intero elenco ordinato. Quindi, nell'esempio seguente, entrambi gli oggetti Header dovrebbero esistere e in sequenza uno sotto l'altro. Se aggiungo un altro oggetto Header con XPos = 2, dovrei quindi avere 3 oggetti nell'elenco, 2 oggetti con XPos = 1 e terzo come XPos = 2
Mayur Kotlikar

1
va bene intendi dire che nel momento stesso in cui un elemento viene inserito nella lista, dovrebbe essere inserito nella posizione corretta secondo l'ordinamento. Per favore correggimi se sbagliato. Fammi dare un'occhiata, tornerò a breve
Dipti Mehta

Si noti che List <T> .Sort utilizza più algoritmi di ordinamento a seconda della dimensione della raccolta e non tutti sono ordinamenti stabili. Quindi gli oggetti aggiunti alla raccolta che si confrontano con l'equivalente potrebbero non apparire nell'ordine in cui sono stati aggiunti.
tono silenzioso

Ho scelto questa opzione in modo da poter smettere di creare quantità eccessive di KeyValuePairs applicando le funzioni di riduzione a un dizionario
Chris Marisic

10

Uso quanto segue:

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

}

Il mio caso di test:

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

Il risultato:

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

Tuple non è disponibile in tutte le versioni di .NET, ma può essere sostituita con KeyValuePair <K, V>
Reuben

6

Il problema è che il design della struttura dati non soddisfa i requisiti: è necessario memorizzare diversi header per lo stesso XPos. Pertanto, SortedList<XPos, value>non dovrebbe avere un valore di Header, ma un valore di List<Header>. È una semplice e piccola modifica, ma risolve tutti i problemi ed evita di creare nuovi problemi come altre soluzioni suggerite (vedi spiegazione sotto):

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

Tieni presente che l'aggiunta di una chiave "divertente", come l'aggiunta di un numero casuale o la finzione che 2 XPo con lo stesso valore siano diversi, porta a molti altri problemi. Ad esempio, diventa difficile o addirittura impossibile rimuovere una particolare intestazione.

Si noti inoltre che le prestazioni di ordinamento sono molto migliori se List<Header>devono essere ordinati solo pochi di tutti Header. Esempio: se sono presenti 100 XPo e ciascuno ha 100 intestazioni, è Headernecessario ordinare 10000 anziché 100 List<Header>.

Ovviamente anche questa soluzione ha uno svantaggio: se ci sono molti XPos con solo 1 Header, è necessario creare altrettante List, che è un po 'di overhead.


Questa è la soluzione più semplice. Controlla anche SortedDictionary, è simile, più veloce in alcuni casi.
Hogan

Questa è davvero una buona soluzione. Si potrebbe facilmente racchiudere quella funzionalità in qualche oggetto di raccolta personalizzato e sarebbe abbastanza utile. Buon pensiero, grazie per aver condiviso Peter!
Adam P

5

Soluzione più semplice (rispetto a tutto quanto sopra): usa SortedSet<T>, accetta una IComparer<SortableKey>classe, quindi implementa il metodo Compare in questo modo:

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
Ho usato SortedSet <T> e T aveva il proprio ID int incrementale che veniva incrementato ad ogni istanza per assicurare che ogni T fosse unico anche con gli altri campi uguali.
Skychan

3
GetHashCode per il confronto è pericoloso. Potrebbe portare a falsi duplicati inaspettati. Potrebbe funzionare la maggior parte del tempo, ma non lo userei mai per qualcosa di serio.
Hogan

4

Grazie mille per il vostro aiuto. Durante la ricerca di più, ho trovato questa soluzione. (Disponibile in Stackoverflow.com in altra domanda)

Innanzitutto, ho creato una classe che incapsulerebbe i miei oggetti per le classi (intestazioni, piè di pagina ecc.)

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

Quindi questa classe dovrebbe mantenere gli oggetti e PosX di ogni oggetto va come 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));

Quello che alla fine ottengo è l'elenco "Sequenza" ordinato.


2

Hai provato Lookup<TKey, TElement>che consentirà chiavi duplicate http://msdn.microsoft.com/en-us/library/bb460184.aspx


Grazie. Il mio problema è che gli oggetti non saranno solo di un tipo (solo non intestazione), quelli possono variare (diciamo piè di pagina, barra laterale, ecc.) Ma ognuno avrà XPos
Mayur Kotlikar

Inoltre, non c'è un costruttore pubblico su Lookupcredo. Qualche buon modo per aggirare questo?
Jeff B

1
@ JeffBridgman dovrai fare affidamento su Linq. Puoi fare ToLookupsu qualsiasi IEnumerable<T>.
nawfal,

7
Sì, consente la duplicazione delle chiavi, ma non mantiene nulla in ordine!
Roman Starkov

2

È possibile utilizzare SortedList, utilizzare il valore per TKey e int (count) per TValue.

Ecco un esempio: una funzione che ordina le lettere di una parola.

    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

Questa classe di raccolta manterrà i duplicati e inserirà l'ordinamento per il duplicato. Il trucco è contrassegnare gli elementi con un valore univoco man mano che vengono inseriti per mantenere un ordinamento stabile. Quindi avvolgiamo tutto in un'interfaccia 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; }
    }
}

una classe di prova

[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();

}

La struttura di etichettatura

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

L'helper del comparatore lambda

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

Il problema è che usi qualcosa come chiave che non è una chiave (perché si verifica più volte).

Quindi se hai coordinate reali dovresti forse prendere il file Point come chiave per la tua SortedList.

Oppure crei un punto in List<List<Header>>cui il tuo primo indice della lista definisce la posizione x e l'indice della lista interna la posizione y (o viceversa se preferisci).


Una chiave può avere più istanze purché non sia una chiave primaria. Almeno questo è quello che mi hanno detto al corso Database che ho seguito.
amalgamare il

1
Questa risposta è un po 'breve, ma spiega correttamente il problema e fornisce la soluzione corretta, cioè utilizzando SortedList <int, List <Header>>. Ciò mantiene ordinata l'intestazione e può memorizzare molte intestazioni nello stesso xPos. Per un esempio di codice cerca la mia risposta. Ho apprezzato questa risposta, poiché punta nella giusta direzione. Per favore, più 1 mia risposta se ritieni che sia utile.
Peter Huber

1

La chiave (gioco di parole) per questo è creare una IComparableclasse basata su-che mantiene l'uguaglianza e l'hashing, ma non è mai paragonabile a 0 se non uguale. Questo può essere fatto e può essere creato con un paio di bonus: ordinamento stabile (ovvero, i valori aggiunti per primi all'elenco ordinato manterranno la loro posizione) eToString() può semplicemente restituire il valore effettivo della stringa chiave.

Ecco una chiave di struttura che dovrebbe fare il trucco:

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

È una bella idea. Ho inserito il concetto in una ICollection personalizzata. Vedere stackoverflow.com/a/21625939/158285
bradgonesurfing

0

Linq.Lookup è interessante e tutto, ma se il tuo obiettivo è semplicemente scorrere le "chiavi" mentre consenti loro di essere duplicate, puoi usare questa struttura:

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

Quindi puoi scrivere:

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

HTH


0

Il trucco è aumentare il tuo oggetto con una chiave univoca. Vedere il seguente test che viene superato. Voglio mantenere i miei punti ordinati in base al loro valore X. Il solo utilizzo di un Point2D nudo nella mia funzione di confronto causerà l'eliminazione dei punti con lo stesso valore X. Quindi inserisco Point2D in una classe di tagging chiamata 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});

}

Le utilità per fare questo lavoro sono

Un comparer che accetta un lambda

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

Uno struct tagging

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

Vedi la mia altra risposta per un involucro completo dei concetti di cui sopra in una classe ICollection personalizzata
bradgonesurfing

0

Ecco come ho risolto il problema. È pensato per essere thread-safe, anche se puoi semplicemente rimuovere locki messaggi se non ne hai bisogno. Si noti inoltre che arbitrario Insertin un indice non è supportato perché potrebbe violare la condizione di ordinamento.

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

Crea una classe e interroga l'elenco:

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

Ecco la mia opinione su questo. Attenzione, qui potrebbero esserci draghi, C # ancora abbastanza nuovo per me.

  • Sono consentite chiavi duplicate, i valori vengono memorizzati in un elenco
  • L'ho usato come una coda ordinata, da cui i nomi e i metodi

Utilizzo:

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

Esiste già una classe Queuein BCL, che rappresenta una raccolta di elementi first-in, first-out. La semantica della tua classe è diversa. La tua classe ha un inizio (dove gli elementi vengono rimossi dalla coda) ma non una fine (un elemento può essere inserito ovunque). Quindi il Enqueuemetodo nella tua classe non ha senso IMHO.
Theodor Zoulias,

@ TheodorZoulias Sì, la denominazione è un po 'di merda qui, ma difficilmente penso che meriti un voto negativo, ha ciò di cui OP ha bisogno ed è solo una questione di rinominare e reimplementare i metodi di input / output. Perché si chiama così? Avevo bisogno di una struttura che posso svuotare dall'inizio in un ciclo while e aggiungere nuovi elementi in base al valore di priorità. Quindi PriorityQueuesarebbe un nome più appropriato.
Solo

L'OP vuole una raccolta ordinabile che consenta chiavi duplicate. La tua classe non è una raccolta , poiché non può essere enumerata. Inoltre non mi piace l'uso di campi pubblici. Non prendere personalmente i voti negativi. Puoi riparare il danno alla reputazione di 5 voti negativi con un solo voto -2 * 5 == +10positivo ( ), quindi non è un grosso problema. :-)
Theodor Zoulias,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.