C # Koleksi yang dapat diurutkan yang memungkinkan kunci duplikat


95

Saya menulis program untuk mengatur urutan di mana berbagai objek akan muncul dalam laporan. Urutannya adalah posisi Y (sel) pada spreadsheet Excel.

Bagian demo kode ada di bawah. Yang ingin saya capai adalah memiliki koleksi, yang memungkinkan saya menambahkan beberapa objek dan saya bisa mendapatkan koleksi yang diurutkan berdasarkan urutannya.

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

Saya tahu bahwa SortedListkehendak tidak mengizinkan ini dan saya telah mencari alternatif. Saya tidak ingin menghilangkan duplikat dan sudah mencoba List<KeyValuePair<int, object>>.

Terima kasih.


1
Apakah koleksi perlu mendukung penyisipan / penghapusan setelah diberikan daftar awal anggota?
Ani

2
Apa yang tidak berhasil saat Anda mencobanya List?
diceguyd30

Saya tidak ingin hanya menyortir dan mendapatkan objeknya. Tetapi saya ingin mendapatkan seluruh daftar yang diurutkan. Jadi dalam contoh di bawah ini, kedua objek Header harus ada dan berurutan satu di bawah yang lain. Jika saya menambahkan objek Header lain dengan XPos = 2, saya harus memiliki 3 objek dalam daftar, 2 objek dengan XPos = 1 dan ketiga sebagai XPos = 2
Mayur Kotlikar

Sekadar catatan: ketika saya menghadapi situasi seperti ini, saya menemukan bahwa Daftar generik yang dikombinasikan dengan perilaku BinarySearch yang tidak banyak diketahui untuk item yang tidak ditemukan bekerja dengan sangat baik.
J Trana

Jawaban:


76

Gunakan IComparer Anda sendiri!

Seperti yang sudah disebutkan di beberapa jawaban lain, Anda harus menggunakan kelas pembanding Anda sendiri. Untuk kepentingan ini saya menggunakan kelas IComparer generik, yang bekerja dengan apa pun yang mengimplementasikan 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
}

Anda akan menggunakannya saat membuat instance SortedList baru, SortedDictionary, dll:

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

Di sini int adalah kunci yang bisa diduplikasi.


40
Tetapi Anda tidak akan dapat menghapus kunci apa pun darinya.
Shashwat

11
Ya itu benar, Shachwat! Anda tidak dapat menggunakan Hapus (kunci) atau IndexOfKey (kunci), karena pembanding tidak pernah mengembalikan 0 ke persamaan kunci sinyal. Tapi Anda mungkin RemoveAt (index) untuk menghapus item jika Anda memiliki indeksnya.
Knasterbax

1
Saya juga mengalami masalah yang sama, saya dulu SortedDictionary. Ini memungkinkan penghapusan juga.
Shashwat

10
Perhatikan bahwa Anda mematahkan refleksivitas pembanding Anda dengan cara ini. Itu bisa (dan akan) merusak banyak hal di BCL.
ghord

1
ini seharusnya mengembalikan -1 untuk menjaga ketertiban
M.kazem Akhgary

16

Anda dapat dengan aman menggunakan List <>. List memiliki metode Sort, yang kelebihan bebannya menerima IComparer. Anda dapat membuat kelas penyortir Anda sendiri sebagai. Berikut contohnya:

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
Saya tidak ingin hanya menyortir dan mendapatkan objeknya. Tetapi saya ingin mendapatkan seluruh daftar yang diurutkan. Jadi dalam contoh di bawah ini, kedua objek Header harus ada dan berurutan satu di bawah yang lain. Jika saya menambahkan objek Header lain dengan XPos = 2, maka saya harus memiliki 3 objek dalam daftar, 2 objek dengan XPos = 1 dan ketiga sebagai XPos = 2
Mayur Kotlikar

1
baiklah maksud Anda mengatakan, bahwa setiap kali elemen dimasukkan ke dalam daftar, itu harus disisipkan pada posisi yang benar sesuai jenisnya. Harap perbaiki saya jika salah. Mari saya lihat, akan kembali sebentar lagi
Dipti Mehta

Perhatikan bahwa List <T> .Sort menggunakan beberapa algoritme pengurutan bergantung pada ukuran koleksi dan tidak semuanya adalah pengurutan yang stabil. Jadi objek yang ditambahkan ke koleksi yang dibandingkan dengan yang setara mungkin tidak muncul dalam urutan penambahannya.
nada diam

saya pergi dengan opsi ini sehingga saya bisa berhenti membuat jumlah KeyValuePairs yang berlebihan dengan menerapkan fungsi pengurangan ke kamus
Chris Marisic

10

Saya menggunakan yang berikut ini:

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

}

Kasus uji saya:

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

Hasil:

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

Tuple tidak tersedia di semua rilis .NET, tetapi dapat diganti dengan KeyValuePair <K, V>
Reuben

6

Masalahnya adalah bahwa desain struktur data tidak sesuai dengan persyaratan: Perlu untuk menyimpan beberapa Header untuk XPos yang sama. Oleh karena itu, SortedList<XPos, value>sebaiknya tidak memiliki nilai Header, tetapi memiliki nilai List<Header>. Ini adalah perubahan sederhana dan kecil, tetapi ini menyelesaikan semua masalah dan menghindari menciptakan masalah baru seperti solusi lain yang disarankan (lihat penjelasan di bawah):

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

Harap dicatat bahwa menambahkan kunci "lucu", seperti menambahkan nomor acak atau berpura-pura bahwa 2 XPos dengan nilai yang sama berbeda menyebabkan banyak masalah lainnya. Misalnya menjadi sulit atau bahkan tidak mungkin untuk menghapus Header tertentu.

Perhatikan juga bahwa kinerja pengurutan jauh lebih baik jika hanya sedikit yang List<Header>harus diurutkan daripada setiap Header. Contoh: Jika ada 100 XPos dan masing-masing memiliki 100 tajuk, 10.000 Headerperlu diurutkan, bukan 100 List<Header>.

Tentu saja, solusi ini juga memiliki kelemahan: Jika ada banyak XPos dengan hanya 1 Header, maka banyak Daftar yang perlu dibuat, yang merupakan overhead.


Ini adalah solusi paling mudah. Periksa juga SortedDictionary, ini serupa, lebih cepat dalam beberapa kasus.
Hogan

Ini adalah solusi yang sangat bagus. Seseorang dapat dengan mudah membungkus fungsionalitas itu ke dalam beberapa objek koleksi khusus dan itu akan sangat berguna. Pikiran yang bagus, terima kasih telah membagikan Peter!
Adam P

5

Solusi paling sederhana (dibandingkan dengan semua hal di atas): gunakan SortedSet<T>, ia menerima IComparer<SortableKey>kelas, lalu terapkan metode Bandingkan dengan cara ini:

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
Saya menggunakan SortedSet <T> dan T memiliki ID int incrementingnya sendiri yang bertambah pada setiap instantiation untuk memastikan setiap T unik bahkan dengan Field lain yang sama.
Skychan

3
GetHashCode untuk perbandingan berbahaya. Dapat menyebabkan duplikat palsu yang tidak terduga. Ini mungkin berhasil sebagian besar waktu, tetapi saya tidak akan pernah menggunakan ini untuk sesuatu yang serius.
Hogan

4

Terima kasih banyak atas bantuannya. Saat mencari lebih banyak, saya menemukan solusi ini. (Tersedia di Stackoverflow.com untuk pertanyaan lain)

Pertama, saya membuat kelas yang akan merangkum objek saya untuk kelas (Headers, Footer dll)

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

Jadi kelas ini seharusnya menampung objek, dan PosX dari setiap objek menjadi Posisi int

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

Apa yang akhirnya saya dapatkan adalah daftar "Urutan" yang diurutkan.


2

Apakah Anda mencoba Lookup<TKey, TElement>yang akan mengizinkan kunci duplikat http://msdn.microsoft.com/en-us/library/bb460184.aspx


Terima kasih. Masalah saya adalah objek tidak hanya dari satu jenis (tidak hanya Header), itu dapat bervariasi (katakanlah Footer, Sidebar dll) tetapi masing-masing akan memiliki XPos
Mayur Kotlikar

Juga, tidak ada konstruktor publik di Lookupsaya percaya. Ada cara bagus untuk mengatasi ini?
Jeff B

1
@JeffBridgman Anda harus mengandalkan Linq. Anda dapat melakukan ToLookupapa saja IEnumerable<T>.
nawfal

8
Ya, ini memungkinkan kunci duplikat, tetapi tidak menyimpan apa pun yang disortir!
Roman Starkov

2

Anda dapat menggunakan SortedList, gunakan nilai Anda untuk TKey, dan int (count) untuk TValue.

Berikut contohnya: Fungsi yang mengurutkan huruf dari sebuah kata.

    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

Kelas koleksi ini akan mempertahankan duplikat dan menyisipkan tata urutan untuk duplikat. Triknya adalah memberi tag item dengan nilai unik saat disisipkan untuk menjaga tata urutan yang stabil. Kemudian kami membungkus semuanya dalam antarmuka 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; }
    }
}

kelas tes

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

}

Struktur pemberian tag

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

Pembantu pembanding 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

Masalahnya adalah Anda menggunakan sesuatu sebagai kunci yang bukan kunci (karena itu terjadi beberapa kali).

Jadi jika Anda memiliki koordinat nyata, Anda mungkin harus mengambilnya Pointsebagai kunci untuk SortedList Anda.

Atau Anda membuat di List<List<Header>>mana indeks daftar pertama Anda mendefinisikan posisi-x dan daftar dalam mengindeks posisi-y (atau sebaliknya jika Anda suka).


Sebuah Kunci dapat memiliki beberapa contoh selama itu bukan kunci utama. Setidaknya itulah yang mereka katakan pada saya di kelas Database yang saya ambil.
menggabungkan

1
Jawaban ini agak pendek, tetapi menjelaskan masalah dengan benar dan memberikan solusi yang tepat, yaitu menggunakan SortedList <int, List <Header>>. Ini mempertahankan tajuk yang diurutkan dan dapat menyimpan banyak tajuk di xPos yang sama. Untuk contoh kode, cari jawaban saya. Saya memetik satu jawaban ini, karena menunjuk ke arah yang benar. Mohon ditambah 1 jawaban saya juga jika Anda merasa itu membantu.
Peter Huber

1

Kuncinya (permainan kata-kata) untuk ini adalah membuat IComparablekelas berbasis yang mempertahankan kesetaraan dan hashing, tetapi tidak pernah dibandingkan dengan 0 jika tidak sama. Ini dapat dilakukan, dan dapat dibuat dengan beberapa bonus - penyortiran stabil (yaitu, nilai yang ditambahkan ke daftar yang diurutkan terlebih dahulu akan mempertahankan posisinya), dan ToString()dapat dengan mudah mengembalikan nilai string kunci yang sebenarnya.

Inilah kunci struct yang harus melakukan trik:

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

Ide yang bagus. Saya membungkus konsep tersebut menjadi ICollection khusus. Lihat stackoverflow.com/a/21625939/158285
bradgonesurfing

0

Linq.Lookup itu keren dan semuanya, tetapi jika target Anda adalah hanya mengulang "kunci" sambil membiarkannya diduplikasi, Anda dapat menggunakan struktur ini:

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

Kemudian Anda bisa menulis:

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

HTH


0

Triknya adalah menambah objek Anda dengan kunci unik. Lihat tes berikut yang lolos. Saya ingin agar poin saya diurutkan berdasarkan nilai X-nya. Hanya menggunakan Point2D telanjang dalam fungsi perbandingan saya akan menyebabkan poin dengan nilai X yang sama dihilangkan. Jadi saya membungkus Point2D dalam kelas penandaan yang disebut 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});

}

Utilitas untuk membuat ini berhasil adalah

Pembanding yang membutuhkan 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);
    }
}

Sebuah struktur penandaan

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

Lihat jawaban saya yang lain untuk bungkus lengkap konsep di atas ke dalam kelas ICollection kustom
bradgonesurfing

0

Beginilah cara saya memecahkan masalah. Ini dimaksudkan agar thread-aman meskipun Anda dapat menghapusnya lockjika Anda tidak membutuhkannya. Juga perhatikan arbitrer Insertpada indeks tidak didukung karena dapat melanggar kondisi pengurutan.

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

Buat kelas dan buat kueri daftarnya:

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

Inilah pendapat saya tentang ini. Awas, di sini mungkin ada komodo, C # masih tergolong baru buat saya.

  • Kunci duplikat diperbolehkan, nilai disimpan dalam daftar
  • Saya menggunakannya sebagai antrian yang diurutkan, oleh karena itu nama dan metodenya

Pemakaian:

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

Sudah ada kelas Queuedi BCL, yang mewakili koleksi item pertama masuk, keluar pertama. Semantik kelas Anda berbeda. Kelas Anda memiliki permulaan (di mana item dikosongkan) tetapi tidak ada akhir (item dapat disisipkan di mana saja). Jadi Enqueuemetode di kelas Anda adalah IMHO tidak ada artinya.
Theodor Zoulias

@TheodorZoulias Yup, penamaan agak sh * t di sini tapi saya rasa itu tidak pantas mendapat downvote, ia memiliki apa yang dibutuhkan OP dan itu hanya masalah mengganti nama dan menerapkan kembali metode input / output. Kenapa disebut seperti ini? Saya membutuhkan struktur yang dapat saya kosongkan dari awal dalam beberapa saat dan menambahkan item baru berdasarkan nilai prioritas. Jadi PriorityQueueakan lebih sesuai namanya.
Solo

OP menginginkan koleksi yang dapat diurutkan yang memungkinkan kunci duplikat. Kelas Anda bukan kumpulan , karena tidak dapat dihitung. Saya juga tidak menyukai penggunaan bidang publik. Jangan mengambil suara negatif itu secara pribadi. Anda dapat memperbaiki kerusakan reputasi dari 5 suara negatif dengan satu suara positif ( -2 * 5 == +10), jadi ini bukan masalah besar. :-)
Theodor Zoulias
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.