Bộ sưu tập có thể sắp xếp trong C # cho phép các khóa trùng lặp


94

Tôi đang viết một chương trình để thiết lập một trình tự trong đó các đối tượng khác nhau sẽ xuất hiện trong báo cáo. Trình tự là vị trí Y (ô) trên bảng tính Excel.

Dưới đây là một phần demo của mã. Những gì tôi muốn đạt được là có một bộ sưu tập, điều này sẽ cho phép tôi thêm nhiều đối tượng và tôi có thể nhận một bộ sưu tập được sắp xếp dựa trên trình tự

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

Tôi biết rằng SortedList sẽ không cho phép điều này và tôi đã tìm kiếm thay thế. Tôi không muốn loại bỏ các bản sao và đã thử List<KeyValuePair<int, object>>.

Cảm ơn.


1
Bộ sưu tập có cần hỗ trợ chèn / xóa sau khi nó được cung cấp danh sách thành viên ban đầu không?
Ani

2
Điều gì không hiệu quả khi bạn đã thử List?
diceguyd 30

Tôi không muốn chỉ phân loại và lấy đối tượng. Nhưng tôi muốn nhận toàn bộ danh sách đã sắp xếp. Vì vậy, trong ví dụ dưới đây, cả hai đối tượng Header phải tồn tại và theo thứ tự bên dưới đối tượng khác. Nếu tôi thêm một đối tượng Tiêu đề với XPos = 2, sau đó tôi sẽ có 3 đối tượng trong danh sách, 2 đối tượng với XPos = 1 và thứ ba như XPos = 2
Mayur Kotlikar

Chỉ cần lưu ý: khi tôi gặp loại tình huống này, tôi thấy rằng Danh sách chung kết hợp với hành vi Tìm kiếm nhị phân ít được biết đến cho các mục không tìm thấy hoạt động kỳ diệu.
J Trana

Câu trả lời:


76

Sử dụng IComparer của riêng bạn!

Giống như đã nêu trong một số câu trả lời khác, bạn nên sử dụng lớp so sánh của riêng mình. Vì lợi ích này, tôi sử dụng một lớp IComparer chung, hoạt động với bất kỳ thứ gì triển khai IComp so sánh được:

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

Bạn sẽ sử dụng nó khi tạo mới SortedList, SortedDictionary, v.v.:

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

Ở đây int là khóa có thể được trùng lặp.


40
Nhưng bạn sẽ không thể xóa bất kỳ khóa nào khỏi nó.
Shashwat

11
Vâng, đúng vậy, Shachwat! Bạn không thể sử dụng Remove (key) hoặc IndexOfKey (key), vì trình so sánh không bao giờ trả về 0 để báo hiệu sự bằng nhau của key. Nhưng bạn có thể RemoveAt (chỉ mục) để xóa các mục nếu bạn có chỉ mục của chúng.
Knasterbax

1
Tôi cũng gặp phải vấn đề tương tự, tôi đã sử dụng SortedDictionary. Nó cũng cho phép loại bỏ.
Shashwat

10
Lưu ý rằng bạn đang phá vỡ tính phản xạ của người so sánh theo cách này. Nó có thể (và sẽ) phá vỡ mọi thứ trong BCL.
bóng ma

1
điều này thực sự sẽ trả về -1 để giữ gìn trật tự
M.kazem Akhgary

16

Bạn có thể yên tâm sử dụng Danh sách <>. Danh sách có phương thức Sắp xếp, phương thức quá tải chấp nhận IComparer. Bạn có thể tạo lớp sắp xếp của riêng mình dưới dạng. Đây là một ví dụ:

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
Tôi không muốn chỉ phân loại và lấy đối tượng. Nhưng tôi muốn nhận toàn bộ danh sách đã sắp xếp. Vì vậy, trong ví dụ dưới đây, cả hai đối tượng Header phải tồn tại và theo thứ tự bên dưới đối tượng khác. Nếu tôi thêm một đối tượng Tiêu đề với XPos = 2, sau đó tôi sẽ có 3 đối tượng trong danh sách, 2 đối tượng với XPos = 1 và thứ ba như XPos = 2
Mayur Kotlikar

1
đúng như ý bạn muốn nói, rằng thời điểm một phần tử được chèn vào danh sách, phần tử đó sẽ được chèn vào đúng vị trí theo cách sắp xếp. Xin vui lòng sửa cho tôi nếu sai. Hãy để tôi có một cái nhìn, sẽ liên lạc lại trong khoảnh khắc ngắn
Dipti Mehta

Lưu ý rằng Danh sách <T> .Sort sử dụng nhiều thuật toán sắp xếp tùy thuộc vào kích thước tập hợp và không phải tất cả chúng đều là sắp xếp ổn định. Vì vậy, các đối tượng được thêm vào bộ sưu tập so sánh với tương đương có thể không xuất hiện theo thứ tự chúng được thêm vào.
giai điệu im lặng

tôi đã sử dụng tùy chọn này để có thể ngừng tạo số lượng KeyValuePairs với việc áp dụng các hàm rút
Chris Marisic

10

Tôi sử dụng như sau:

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

}

Trường hợp thử nghiệm của tôi:

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

Đầu ra:

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

Tuple không có sẵn trong tất cả các phiên của .NET, nhưng có thể được thay thế bởi KeyValuePair <K, V>
Reuben

6

Vấn đề là thiết kế cấu trúc dữ liệu không phù hợp với yêu cầu: Cần phải lưu trữ nhiều Header cho cùng một XPos. Do đó, SortedList<XPos, value>không nên có giá trị của Header, mà là giá trị của List<Header>. Đó là một thay đổi đơn giản và nhỏ, nhưng nó giải quyết được tất cả các vấn đề và tránh tạo ra các vấn đề mới như các giải pháp được đề xuất khác (xem giải thích bên dưới):

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

Xin lưu ý rằng việc thêm một phím "vui nhộn", như thêm một số ngẫu nhiên hoặc giả vờ rằng 2 XPos có cùng giá trị khác nhau sẽ dẫn đến nhiều vấn đề khác. Ví dụ, nó trở nên khó khăn hoặc thậm chí không thể xóa một Header cụ thể.

Cũng lưu ý rằng hiệu suất sắp xếp tốt hơn nhiều nếu chỉ có một số ít List<Header>phải được sắp xếp hơn mọi Header. Ví dụ: Nếu có 100 XPos và mỗi XP có 100 tiêu đề, 10000 Headercần phải được sắp xếp thay vì 100 List<Header>.

Tất nhiên, giải pháp này cũng có một nhược điểm: Nếu có nhiều XPos chỉ có 1 Header, thì cần phải tạo nhiều Danh sách, điều này gây tốn kém một số chi phí.


Đây là giải pháp đơn giản nhất. Cũng kiểm tra SortedDictionary, nó cũng tương tự, nhanh hơn trong một số trường hợp.
Hogan

Đây là một giải pháp thực sự tốt. Người ta có thể dễ dàng gói chức năng đó vào một số đối tượng thu thập tùy chỉnh và nó sẽ khá hữu ích. Ý kiến ​​hay, cảm ơn vì đã chia sẻ Peter!
Adam P

5

Giải pháp đơn giản nhất (so với tất cả các cách trên): sử dụng SortedSet<T>, nó chấp nhận một IComparer<SortableKey>lớp, sau đó triển khai phương thức So sánh theo cách này:

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
Tôi đã sử dụng SortedSet <T> và T có int ID tăng dần của riêng nó tăng dần trên mỗi lần khởi tạo để đảm bảo mỗi T là duy nhất ngay cả với (các) Trường khác là giống nhau.
Skychan

3
GetHashCode để so sánh là nguy hiểm. Có thể dẫn đến trùng lặp không mong muốn. Nó có thể hoạt động hầu hết thời gian, nhưng tôi sẽ không bao giờ sử dụng nó cho bất cứ điều gì nghiêm trọng.
Hogan

4

Cảm ơn sự giúp đỡ của bạn. Trong khi tìm kiếm thêm, tôi đã tìm thấy giải pháp này. (Có sẵn trong Stackoverflow.com trong câu hỏi khác)

Đầu tiên, tôi đã tạo một lớp sẽ đóng gói các đối tượng của tôi cho các lớp (Đầu trang, Chân trang, v.v.)

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

Vì vậy, lớp này được cho là giữ các đối tượng và PosX của mỗi đối tượng sẽ là int Vị trí

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

Những gì cuối cùng tôi nhận được là danh sách "Trình tự" được sắp xếp.


2

Bạn đã thử Lookup<TKey, TElement>điều đó sẽ cho phép các khóa trùng lặp http://msdn.microsoft.com/en-us/library/bb460184.aspx


Cảm ơn. Vấn đề của tôi là các đối tượng sẽ không chỉ của một loại (chỉ cần không Header), những người có thể khác nhau (cho phép nói Footer, Sidebar vv) nhưng mỗi người sẽ có XPos
Mayur Kotlikar

Ngoài ra, Lookuptôi tin rằng không có nhà xây dựng công khai nào . Bất kỳ cách nào tốt để giải quyết vấn đề này?
Jeff B

1
@JeffBridgman bạn sẽ phải dựa vào Linq. Bạn có thể làm ToLookuptrên bất kỳ IEnumerable<T>.
nawfal 23/09/13

7
Có, nó cho phép các khóa trùng lặp, nhưng nó không giữ bất cứ thứ gì được sắp xếp!
Roman Starkov

2

Bạn có thể sử dụng SortedList, sử dụng giá trị của bạn cho TKey và int (count) cho TValue.

Đây là một mẫu: Một chức năng sắp xếp các chữ cái của một từ.

    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

Lớp tập hợp này sẽ duy trì các bản sao và chèn thứ tự sắp xếp cho bản sao. Mẹo là gắn thẻ các mục có giá trị duy nhất khi chúng được chèn vào để duy trì thứ tự sắp xếp ổn định. Sau đó, chúng tôi kết thúc tất cả trong một giao diện 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; }
    }
}

một lớp học thử nghiệm

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

}

Cấu trúc gắn thẻ

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

Trình trợ giúp lambda so sánh

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

Vấn đề là bạn sử dụng thứ gì đó làm chìa khóa mà không phải là chìa khóa (vì nó xảy ra nhiều lần).

Vì vậy, nếu bạn có tọa độ thực, bạn có thể nên lấy nó Pointlàm khóa cho SortedList của mình.

Hoặc bạn tạo một List<List<Header>>nơi chỉ mục danh sách đầu tiên của bạn xác định vị trí x và danh sách bên trong chỉ mục vị trí y (hoặc ngược lại nếu bạn muốn).


Một Khóa có thể có nhiều phiên bản miễn là nó không phải là khóa chính. Ít nhất đó là những gì họ nói với tôi trong lớp Cơ sở dữ liệu mà tôi đã tham gia.
amalgamate

1
Câu trả lời này hơi ngắn, nhưng nó giải thích vấn đề một cách hợp lý và cung cấp giải pháp chính xác, tức là sử dụng SortedList <int, List <Header>>. Điều này duy trì tiêu đề được sắp xếp và có thể lưu trữ nhiều tiêu đề trên cùng một xPos. Để có một mẫu mã, hãy tìm câu trả lời của tôi. Tôi đã cộng một câu trả lời này, vì nó chỉ đúng hướng. Vui lòng cộng thêm 1 câu trả lời của tôi nếu bạn cảm thấy nó hữu ích.
Peter Huber,

1

Chìa khóa (ý định chơi chữ) của điều này là tạo ra một IComparablelớp dựa trên cơ sở duy trì sự bình đẳng và băm, nhưng không bao giờ so sánh với 0 nếu không bằng nhau. Điều này có thể được thực hiện và có thể được tạo ra với một vài phần thưởng - sắp xếp ổn định (nghĩa là, các giá trị được thêm vào danh sách được sắp xếp trước sẽ duy trì vị trí của chúng) và ToString()có thể chỉ cần trả về giá trị chuỗi khóa thực tế.

Đây là một khóa cấu trúc sẽ thực hiện thủ thuật:

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

Đó là một ý tưởng tốt. Tôi đã đưa khái niệm này vào một ICollection tùy chỉnh. Xem stackoverflow.com/a/21625939/158285
bradgonesurfing

0

Linq.Lookup rất tuyệt và tất cả, nhưng nếu mục tiêu của bạn chỉ là lặp lại các "phím" trong khi cho phép chúng được sao chép, bạn có thể sử dụng cấu trúc này:

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

Sau đó, bạn có thể viết:

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

HTH


0

Bí quyết là tăng cường đối tượng của bạn bằng một khóa duy nhất. Xem bài kiểm tra sau đây. Tôi muốn giữ các điểm của mình được sắp xếp theo giá trị X của chúng. Chỉ sử dụng Point2D trần trong hàm so sánh của tôi sẽ khiến các điểm có cùng giá trị X bị loại bỏ. Vì vậy, tôi bọc Point2D trong một lớp gắn thẻ có tên là 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});

}

Các tiện ích để thực hiện công việc này là

Một trình so sánh sử dụng 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);
    }
}

Một cấu trúc gắn thẻ

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

Xem câu trả lời khác của tôi cho một bọc hoàn chỉnh về các khái niệm trên vào một lớp tùy chỉnh ICollection
bradgonesurfing

0

Đây là cách tôi giải quyết vấn đề. Nó có nghĩa là an toàn cho chuỗi mặc dù bạn có thể chỉ cần loại bỏ các locks nếu bạn không cần. Cũng lưu ý rằng tùy ý Inserttại một chỉ mục không được hỗ trợ vì điều đó có thể vi phạm điều kiện sắp xếp.

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

Tạo một lớp và truy vấn danh sách:

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

Đây là công việc của tôi. Cần biết rằng, đây có thể là những con rồng, C # vẫn còn khá mới đối với tôi.

  • Các khóa trùng lặp được cho phép, các giá trị được lưu trữ trong danh sách
  • Tôi đã sử dụng nó như một hàng đợi được sắp xếp, do đó tên và phương thức

Sử dụng:

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

Đã có một lớp Queuetrong BCL, đại diện cho một tập hợp các mục nhập trước, xuất trước. Ngữ nghĩa của lớp học của bạn là khác nhau. Lớp của bạn có phần bắt đầu (nơi các mục được giảm giá trị) nhưng không có phần cuối (một mục có thể được chèn vào bất kỳ đâu). Vì vậy, Enqueuephương thức trong lớp của bạn là IMHO vô nghĩa.
Theodor Zoulias

@TheodorZoulias Yup, đặt tên ở đây hơi lố nhưng tôi hầu như không nghĩ rằng nó xứng đáng nhận được sự ủng hộ, nó có những gì OP cần và vấn đề chỉ là đổi tên và thực hiện lại các phương thức nhập / xuất. Tại sao nó được gọi như thế này? Tôi cần một cấu trúc mà tôi có thể làm trống ngay từ đầu trong vòng lặp while và thêm các mục mới dựa trên giá trị ưu tiên. Vì vậy, PriorityQueuesẽ là tên thích hợp hơn.
Solo

OP muốn một bộ sưu tập có thể sắp xếp cho phép các khóa trùng lặp. Lớp của bạn không phải là một tập hợp , vì nó không thể được liệt kê. Tôi cũng không thích việc sử dụng các trường công cộng. Đừng nhận phiếu phản đối cá nhân. Bạn có thể sửa chữa những thiệt hại về danh tiếng của 5 lượt phản đối chỉ với một lượt ủng hộ ( -2 * 5 == +10), vì vậy nó không phải là vấn đề lớn. :-)
Theodor Zoulias
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.