C#可排序集合,允许重复键

May*_*kar 82 .net c# linq collections sortedlist

我正在编写一个程序来设置一个序列,其中各种对象将出现在报告中.序列是Excel电子表格中的Y位置(单元格).

代码的演示部分如下.我想要完成的是拥有一个集合,这将允许我添加多个对象,我可以根据序列获得一个排序的集合

SortedList list = new SortedList();

Header h = new Header();
h.XPos = 1;
h.name = "Header_1";
list.Add(h.XPos, h);

h = new Header();
h.XPos = 1;
h.name = "Header_2";
list.Add(h.XPos, h);
Run Code Online (Sandbox Code Playgroud)

我知道SortedList不允许这样,我一直在寻找替代品.我不想消除重复,已经尝试过了List<KeyValuePair<int, object>>.

谢谢.

Kna*_*bax 67

使用您自己的IComparer!

就像在其他一些答案中已经说过的那样,你应该使用你自己的比较器类.为此,我使用了一个通用的IComparer类,它可以与任何实现IComparable的东西一起使用:

/// <summary>
/// Comparer for comparing two keys, handling equality as beeing greater
/// Use this Comparer e.g. with SortedLists or SortedDictionaries, that don't allow duplicate keys
/// </summary>
/// <typeparam name="TKey"></typeparam>
public class DuplicateKeyComparer<TKey>
                :
             IComparer<TKey> where TKey : IComparable
{
    #region IComparer<TKey> Members

    public int Compare(TKey x, TKey y)
    {
        int result = x.CompareTo(y);

        if (result == 0)
            return 1;   // Handle equality as beeing greater
        else
            return result;
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

在实例化新的SortedList,SortedDictionary等时,您将使用它:

SortedList<int, MyValueClass> slist = new SortedList<int, MyValueClass>(new DuplicateKeyComparer<int>());
Run Code Online (Sandbox Code Playgroud)

这里int是可以重复的键.

  • 但是你将无法从中删除任何密钥. (31认同)
  • 是的,Shachwat!您不能使用Remove(key)或IndexOfKey(key),因为比较器从不返回0来表示信号键相等.但是如果你有索引,你可能会删除项目(索引)以删除项目. (11认同)
  • 请注意,您正在通过这种方式打破比较器的[反射性](http://en.wikipedia.org/wiki/Reflexive_relation).它可以(并且将会)破坏BCL中的东西. (8认同)
  • 这实际上应该返回 -1 以保持秩序 (2认同)

小智 13

您可以安全地使用List <>.List有一个Sort方法,其重载接受IComparer.您可以创建自己的分拣机类.这是一个例子:

private List<Curve> Curves;
this.Curves.Sort(new CurveSorter());

public class CurveSorter : IComparer<Curve>
{
    public int Compare(Curve c1, Curve c2)
    {
        return c2.CreationTime.CompareTo(c1.CreationTime);
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 9

我使用以下内容:

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

}
Run Code Online (Sandbox Code Playgroud)

我的测试用例:

[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);
    }
Run Code Online (Sandbox Code Playgroud)

输出:

1:cab
1:coconut
1:car
1:cat
2:door
2:dog
3:elephant
Run Code Online (Sandbox Code Playgroud)

  • Tuple 并非在 .NET 的所有版本中都可用,但可以用 KeyValuePair&lt;K,V&gt; 替代 (2认同)

Pet*_*ber 8

问题是数据结构设计不符合要求:需要为同一个XPos存储多个Headers。因此,SortedList<XPos, value>不应具有 的值Header,而是具有 的值List<Header>。这是一个简单而小的更改,但它解决了所有问题并避免了像其他建议的解决方案一样产生新问题(请参阅下面的解释):

using System;
using System.Collections.Generic;

namespace TrySortedList {
  class Program {

    class Header {
      public int XPos;
      public string Name;
    }

    static void Main(string[] args) {
      SortedList<int, List<Header>> sortedHeaders = new SortedList<int,List<Header>>();
      add(sortedHeaders, 1, "Header_1");
      add(sortedHeaders, 1, "Header_2");
      add(sortedHeaders, 2, "Header_3");
      foreach (var headersKvp in sortedHeaders) {
        foreach (Header header in headersKvp.Value) {
          Console.WriteLine(header.XPos + ": " + header.Name);
        }
      }
    }

    private static void add(SortedList<int, List<Header>> sortedHeaders, int xPos, string name) {
      List<Header> headers;
      if (!sortedHeaders.TryGetValue(xPos, out headers)){
        headers = new List<Header>();
        sortedHeaders[xPos] = headers;
      }
      headers.Add(new Header { XPos = xPos, Name = name });
    }
  }
}

Output:
1: Header_1
1: Header_2
2: Header_3
Run Code Online (Sandbox Code Playgroud)

请注意,添加“有趣”的键,例如添加随机数或假装具有相同值的 2 个 XPos 不同会导致许多其他问题。例如,删除特定的标题变得困难甚至不可能。

另请注意,如果只List<Header>需要排序少数比每个Header. 示例:如果有 100 个 XPos 并且每个都有 100 个标题,则 10000Header需要排序而不是 100 List<Header>

当然,这个方案也有一个缺点:如果有很多只有 1 个 Header 的 XPos,那么需要创建很多 List,这是一些开销。


kno*_*cte 6

最简单的解决方案(与上述所有相比):use SortedSet<T>,它接受一个IComparer<SortableKey>类,然后以这种方式实现Compare方法:

public int Compare(SomeClass x, SomeClass y)
{
    var compared = x.SomeSortableKeyTypeField.CompareTo(y.SomeSortableKeyTypeField);
    if (compared != 0)
        return compared;

    // to allow duplicates
    var hashCodeCompare = x.GetHashCode().CompareTo(y.GetHashCode());
    if (hashCodeCompare != 0)
        return hashCodeCompare;

    if (Object.ReferenceEquals(x, y))
        return 0;

    // for weird duplicate hashcode cases, throw as below or implement your last chance comparer
    throw new ComparisonFailureException();

}
Run Code Online (Sandbox Code Playgroud)

  • 我使用了SortedSet <T>并且T拥有它自己的递增int ID,在每个实例化时递增以确保每个T都是唯一的,即使其他Field是相同的. (4认同)
  • GetHashCode进行比较很危险。可能导致意外的虚假重复。它可能在大多数时间都有效,但是我绝不会将它用于任何严重的事情。 (2认同)

May*_*kar 4

非常感谢你的帮助。在进行更多搜索时,我找到了这个解决方案。(可在 Stackoverflow.com 的其他问题中找到)

首先,我创建了一个类,它将封装我的类对象(页眉、页脚等)

public class MyPosition
{
    public int Position { get; set; }
    public object MyObjects{ get; set; }
}
Run Code Online (Sandbox Code Playgroud)

所以这个类应该保存对象,并且每个对象的 PosX 都是 int Position

List<MyPosition> Sequence= new List<MyPosition>();
Sequence.Add(new MyPosition() { Position = 1, Headerobject });
Sequence.Add(new MyPosition() { Position = 2, Headerobject1 });
Sequence.Add(new MyPosition() { Position = 1, Footer });

League.Sort((PosA, PosB) => PosA.Position.CompareTo(PosB.Position));
Run Code Online (Sandbox Code Playgroud)

最终我得到的是排序后的“序列”列表。