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; }
}
您应该只从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;
}
}