c# IComparer在比较相同字符串时返回意外结果

本文关键字:返回 意外 结果 字符串 IComparer 比较 | 更新日期: 2023-09-27 18:09:56

我有一个情况,我的所有列表成员有相同的ID (ID是字符串而不是整数)。作为业务规则的一部分,我需要按升序对列表进行排序。我最初的实现与下面非常相似。我希望在应用排序后得到不变的列表,因为所有列表成员都有相同的ID,但令我惊讶的是结果是不同的。

下面是我在排序前的原始列表。

Id: D1.2名称:厚头龙
Id: D1.2名称:Amargasaurus
Id: D1.2名称:Mamenchisaurus
Id: D1.2名称:恐爪龙
Id: D1.2名称:Coelophysis
Id: D1.2名称:Oviraptor
Id: D1.2名称:Tyrannosaur

使用备用比较器排序:

Id: D1.2名称:厚头龙
Id: D1.2名称:Oviraptor
Id: D1.2名称:Coelophysis
Id: D1.2名称:恐爪龙
Id: D1.2名称:Mamenchisaurus
Id: D1.2名称:Amargasaurus
Id: D1.2名称:Tyrannosaur

class Program
{
    static void Main(string[] args)
    {
        new ComparerIssue().MainMethod();
        Console.ReadKey();
    }
}
internal class DinoComparer : IComparer<Dinosaur>
{
    public int Compare(Dinosaur dinosaur1, Dinosaur dinosaur2)
    {
        return Compare(dinosaur1.Id, dinosaur2.Id);
    }
    private int Compare(string x, string y)
    {
        if (x == y)
        {
            return 1; //I have tried using 1 and 0; -1 throws exception
        }
        return x.CompareTo(y);
    }
}
public class ComparerIssue
{
    public void MainMethod()
    {
        List<Dinosaur> dinosaurs = new List<Dinosaur>();
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Pachycephalosaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Amargasaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Mamenchisaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Deinonychus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Coelophysis" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Oviraptor" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Tyrannosaur" });
        Display(dinosaurs);
        DinoComparer dc = new DinoComparer();
        Console.WriteLine("'nSort with alternate comparer:");
        dinosaurs.Sort(dc);
        Display(dinosaurs);
    }
    private static void Display(IEnumerable<Dinosaur> list)
    {
        Console.WriteLine();
        foreach (Dinosaur dinosaur in list)
        {
            Console.WriteLine("Id: " + dinosaur.Id + " Name: " + dinosaur.Name);
        }
    }
}
public class Dinosaur
{
    public string Id { get; set; }
    public string Name { get; set; }
}

c# IComparer在比较相同字符串时返回意外结果

您应该只从private int Compare(string x, string y)方法返回return x.CompareTo(y);,因为您只基于字符串进行比较…

:

private int Compare(string x, string y)
{
    return x.CompareTo(y);
}

希望有帮助,伊凡

您违反了IComparer的隐含契约,因为Compare(dino1,dino2)Compare(dino2,dino1)将返回dino1大于dino2, dino2大于dino1。由于您没有正确定义顺序,因此结果最多只能是"随机的"。

如果你不能定义一个完全基于ID值的总订单,那么仅仅使用ID值不能作为你的IComparer实现的基础。

你违反了IComparable的合同;当你的id相等时,你实际上是在说一个大于另一个(所以需要排序)

来自文档:

小于零该对象小于其他参数。0这个对象等于其他对象。大于零该对象大于其他对象。

Compare的另一种实现是:

private int Compare(string x, string y)
{
    return x.CompareTo(y);
    // There would be potential to do secondary sorts if the line above only returned zero - you'd obviously need to capture and test the result...
}

我个人会使用icesar的答案,但使用静态字符串。比较方法:

return string.Compare(x, y);

这使得比较更"安全",你不必检查是否为空。

或者一个简单的LINQ语句可以完成这项工作:

myList = myList.OrderBy(p => p.ID).ThenBy(p => p.Name);

您还应该注意,一旦您在列表中获得一些项目,按ID作为字符串排序将导致错误的结果;21将放在3之前。您可能需要考虑在某个阶段将其强制转换为int

From MSDN:

这个方法使用Array。Sort,它使用快速排序算法。这实现执行不稳定排序;也就是说,如果两个元素是相等时,它们的顺序可能是而不是。相反,一个稳定的排序保留相等元素的顺序。

(重点)

这正是你所看到的。

编辑正如其他人所暗示的,您可以使用linq方法OrderBy,它确实执行稳定的排序:

var d2 = dinosaurs.OrderBy(d => d.Id).ToList();

遗憾的是,据我所知,框架中没有实现稳定的排序方法。你得自己做这件事。

这http://www.csharp411.com/c-stable-sort/是一个很好的稳定排序方法的例子。

衷心感谢大家的反馈。我已经实现了稳定的排序使用插入方法找到http://www.csharp411.com/c-stable-sort/。我包括最后的代码供参考。

internal class DinoComparer : IComparer<Dinosaur>
{
    public int Compare(Dinosaur dinosaur1, Dinosaur dinosaur2)
    {
        return Compare(dinosaur1.Id, dinosaur2.Id);
    }
    private int Compare(string x, string y)
    {
        return x.CompareTo(y);
    }
}
public class ComparerIssue
{
    public void MainMethod()
    {
        List<Dinosaur> dinosaurs = new List<Dinosaur>();
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Pachycephalosaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Amargasaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Mamenchisaurus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Deinonychus" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Coelophysis" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Oviraptor" });
        dinosaurs.Add(new Dinosaur() { Id = "D1.2", Name = "Tyrannosaur" });
        Display(dinosaurs);
        //Console.WriteLine("'nSort with unstable comparer:");
        //dinosaurs.Sort(new DinoComparer());
        Console.WriteLine("'nSort with stable comparer:");
        dinosaurs = (List<Dinosaur>)InsertionSort.Sort(dinosaurs, new DinoComparer().Compare);
        Display(dinosaurs);
    }
    private static void Display(IEnumerable<Dinosaur> list)
    {
        Console.WriteLine();
        foreach (Dinosaur dinosaur in list)
        {
            Console.WriteLine("Id: " + dinosaur.Id + " Name: " + dinosaur.Name);
        }
    }
}
public class Dinosaur
{
    public string Id { get; set; }
    public string Name { get; set; }
}
public class InsertionSort
{
    public static IList<T> Sort<T>(IList<T> list, Comparison<T> comparison)
    {
        if (list == null)
            throw new ArgumentNullException("list");
        if (comparison == null)
            throw new ArgumentNullException("comparison");
        int count = list.Count;
        for (int j = 1; j < count; j++)
        {
            T key = list[j];
            int i = j - 1;
            for (; i >= 0 && comparison(list[i], key) > 0; i--)
            {
                list[i + 1] = list[i];
            }
            list[i + 1] = key;
        }
        return list;
    }
}