C# SortedSet<T> and equality

本文关键字:and equality gt lt SortedSet | 更新日期: 2023-09-27 18:18:31

我对SortedSet的行为有点困惑,参见以下示例:

public class Blah
{
    public double Value { get; private set; }
    public Blah(double value)
    {
        Value = value;
    }
}
public class BlahComparer : Comparer<Blah>
{
    public override int Compare(Blah x, Blah y)
    {
        return Comparer<double>.Default.Compare(x.Value, y.Value);
    }
}
public static void main()
{
    var blahs = new List<Blah> {new Blah(1), new Blah(2), 
                                new Blah(3), new Blah(2)}
    //contains all 4 entries
    var set = new HashSet<Blah>(blahs); 
    //contains only Blah(1), Blah(2), Blah(3)
    var sortedset = new SortedSet<Blah>(blahs, new BlahComparer());
}

所以如果Compare(x,y)返回0,SortedSet将丢弃条目。我可以防止这种情况,这样我的SortedSet行为像HashSet和丢弃条目只有当Equals()返回true?

C# SortedSet<T> and equality

描述

SortedSet:您需要存储许多元素,并且您希望以有序的顺序存储它们,并且从数据结构中消除所有重复的。SortedSet类型是c#语言和。net框架中System.Collections.Generic命名空间的一部分,它提供了这个功能。

根据MSDN Compare方法返回

  • 小于0 如果x小于y
  • 0 如果x等于y
  • 大于0 如果x大于y

的更多信息
  • Dotnetperls - c# SortedSet示例
  • MSDN: Compare Method

更新

如果你的Bla类实现了IComparable,你想要你的列表排序,你可以这样做。

var blahs = new List<Blah> {new Blah(1), new Blah(2), 
                            new Blah(3), new Blah(2)};
blahs.Sort();

如果你的BlaNOT实现了IComparable,你想要你的列表排序,你可以使用Linq(系统。

blahs = blahs.OrderBy(x => x.MyProperty).ToList();

如果在值相等且Compare方法返回0时提供另一种比较,则可以这样做。在大多数情况下,这可能只是推迟问题而不是解决问题。正如其他人所注意到的,SortedSet会丢弃重复项,当您提供自定义比较器时,它会使用该比较器来确定重复项。

    static void Main(string[] args)
    {
        var blahs = new List<Blah>
                        {
                            new Blah(1, 0), new Blah(2, 1),
                            new Blah(3, 2), new Blah(2, 3)
                        };
        blahs.Add(blahs[0]);
        //contains all 4 entries
        var set = new HashSet<Blah>(blahs);
        //contains all 4 entries
        var sortedset = new SortedSet<Blah>(blahs, new BlahComparer());
    }
}
public class Blah
{
    public double Value { get; private set; }
    public Blah(double value, int index)
    {
        Value = value;
        Index = index;
    }
    public int Index { get; private set; }
    public override string ToString()
    {
        return Value.ToString();
    }
}
public class BlahComparer : Comparer<Blah>
{
    public override int Compare(Blah x, Blah y)
    {
        // needs null checks
        var referenceEquals = ReferenceEquals(x, y);
        if (referenceEquals)
        {
            return 0;
        }
        var compare = Comparer<double>.Default.Compare(x.Value, y.Value);
        if (compare == 0)
        {
            compare = Comparer<int>.Default.Compare(x.Index, y.Index);
        }
        return compare;
    }
}

你找不到其他Blah(2),因为你使用的是Set

Set - A collection of well defined and **distinct** objects
例如,

MultiSet允许复制对象。

听起来您想要的是基于属性的排序,但是重复检查应该基于引用相等性。要做到这一点(如果您不介意比较器的内存消耗会随着时间的推移而增加),我们可以向比较器添加一个回退,它根据实例的唯一id计算比较结果:

public class BlahComparer : Comparer<Blah>
{
    private readonly ObjectIDGenerator _idGenerator = new();
    public override int Compare(Blah x, Blah y)
    {
        int compareResult = Comparer<double>.Default.Compare(x.Value, y.Value);
        if (compareResult == 0)
        {
            // Comparing hash codes is optional and is only done in order to potentially avoid using _idGenerator further below which is better for memory consumption.
            compareResult =
                Comparer<int>.Default.Compare(RuntimeHelpers.GetHashCode(x), RuntimeHelpers.GetHashCode(y));
            if (compareResult == 0)
            {
                // HashCodes are the same but it might actually still be two different objects, so compare unique IDs:
                compareResult = Comparer<long>.Default.Compare(_idGenerator.GetId(x, out bool _), _idGenerator.GetId(y, out bool _)); // This increases the memory consumption of the comparer for every newly encountered Blah
            }
        }
        return compareResult;
    }
}