为什么我的OrderBy会永远运行这个比较器?

时间:2016-06-15 00:04:02

标签: c# .net algorithm sorting

我有一个班级,

public class NullsAreLast : IComparer<int?>
{
    public int Compare (int? x, int? y)
    {
        if(y == null)
            return -1;
        else if(x == null)
            return 1;
        else
            return (int)x - (int)y;
    }
}

对于它应该如何工作是不言自明的。

每当我跑

arr.OrderBy(i => i, new NullsAreLast())

null中至少有两个arr值,它会永远运行!知道为什么吗?

2 个答案:

答案 0 :(得分:3)

请记住,排序算法可能会在整个序列的排序过程中多次比较相同的两个值。因此,了解所有三种可能的结果非常重要:小于,大于和等于。

这对于(结束时)整数比较(大多数)来说是很好的(减法运算)。使用浮点数而不是整数时,有一些奇怪/罕见的边缘情况,并且无论如何调用.CompareTo()是首选做法,但在这种情况下减法通常是足够好的。但是,这里的null检查是一个真正的问题。

考虑一下列表几乎完成排序会发生什么。您有两个null值,它们都已经到达列表的前面;算法只需要验证它们是否处于正确的位置。由于xy都是null,因此您的函数应返回0。它们是等价的(至少为此目的)。相反,代码始终返回-1y值始终小于x值,因此算法始终认为它仍然需要交换它们。它交换,并试图再次做同样的事情。然后再次。然后再次。然后再次。它永远无法完成。

请改为尝试:

public class NullsAreLast : IComparer<int?>
{
    public int Compare (int? x, int? y)
    {
        if(!y.HasValue)
        {
            if (!x.HasValue) return 0;
            return -1;
        }
        if(!x.HasValue) return 1;
        return x.Value.CompareTo(y.Value);
    }
}

答案 1 :(得分:0)

比较方法结束时的减号操作不适合进行比较。你需要处理三种可能性 - x更大,y更大,或者它们是相同的。

  

<强> MSDN

     

比较两个对象并返回一个值,指示是否有一个   小于,等于或大于另一个。

使用此代码,假设X为1000,Y为15.您的结果将是985,这在这里没有意义。

鉴于你的代码和方法名称,我猜你的意思是:

public class NullsAreLast : IComparer<int?>
{
    public int Compare (int? x, int? y)
    {
        if(y == null)
            return -1;
        else if(x == null)
            return 1;
        else{
            int diff = x - y;
            if (diff == 0) return 0;  //same
            if (diff < 0) return 1;   //y was bigger
            if (diff > 0) return -1;  //x was bigger
        }   
    }
}

你甚至可以把它粉碎成一个可怕的单行:

return (y==null?-1:(x==null?1:(x-y==0?0:(x-y<0?1:-1))));