【问题标题】:Order of List using Linq is not the same as sort使用 Linq 的列表顺序与排序不同
【发布时间】:2011-04-06 14:50:18
【问题描述】:

我想确认一下,我正在尝试使用 Linq 对我的班级列表进行排序。但是当我使用排序功能时,数据的顺序似乎与排序方式不同。

假设列表包含 4 个 ComputeItem 并且它们的所有A都设置为1,所有的B、C、D都设置为零。

案例 1:

ItemList =
    ItemList
        .OrderByDescending(m => m.A)
        .ThenBy(m => m.B)
        .ThenBy(m => m.C)
        .ThenBy(m => m.D)
        .ToList<ComputeItem>();

案例 2:

ItemList.Sort(
    delegate(ComputeItem item1, ComputeItem item2)
    {
        if (item1.A == item2.A)
        {
            if (item1.B == item2.B)
            {
                if (item1.C == item2.C)
                {
                    return item1.D - item2.D;
                }
                else
                {
                    return item1.C - item2.C;
                }
            }
            else
            {
                return item1.B - item2.B;
            }
        }
        else
        {
            return item2.A - item1.A;
        }
    }
);

第一次排序的结果是它没有移动任何东西。
第二次排序的结果是将其排序为不同的顺序。
原始订单 [1, 2, 3, 4]
案例 1 新订单 [1, 2, 3, 4]
CASE 2 新订单 [3, 4, 1, 2]

现在问题出在我使用 CASE2 并尝试将其迁移到 CASE 1 之前。但行为不会比以前发生巨大变化。知道为什么 CASE 2 改变了顺序吗?

【问题讨论】:

    标签: c# linq list sorting


    【解决方案1】:

    OrderByOrderByDescendingThenByThenByDescending使用的排序算法是stableQuickSort。来自MSDN documentation

    此方法执行稳定排序; 也就是说,如果两个元素的键 相等,元素的顺序 被保留。

    List&lt;T&gt;.Sort 使用不稳定版本的快速排序,它不一定保留相等元素的原始顺序。同样,来自MSDN documentation

    此方法使用Array.Sort,即 使用快速排序算法。这 实现执行不稳定 种类;也就是说,如果两个元素是 相等,他们的顺序可能不是 保存。

    这解释了您所看到的差异。显然,两者的最终结果将是项目以您的比较机制规定的方式排序。对于List&lt;T&gt;.Sort,只有相等元素出现的顺序是未指定的。

    好消息是您将不稳定排序稳定排序。我很难想象这对您来说可能是一个重大变化(什么样的软件要求某种不稳定?)。如果有的话,它应该使您的程序更具可预测性。

    【讨论】:

    • 这是我第一次遇到稳定和不稳定的快速排序。但这是有道理的。
    • 向后兼容。在很多情况下,您希望用户使用不同的实现从相同的输入获得完全相同的结果。稳定与不稳定的排序不允许这样做。尽管不稳定的排序不能保证行为,但对于给定的一组输入,它很可能每次都给出一致的结果——这是用户期望的,并且可能需要。
    • @Tom W:公平点。显然我无法确切知道 OP 的情况。也许我的语言到底有一点过于乐观的考虑。但我仍然倾向于认为,在大多数情况下,从稳定排序转移到不稳定排序会比向相反方向移动更可能对用户造成破坏。但可以肯定的是,依赖不稳定排序的特定结果是可能的。不过,我认为您可能夸大了这种可能性。再说一次,也许我低估了它。
    【解决方案2】:

    我认为拉胡尔一针见血。 对于您的案例 2,我个人认为以下内容更具可读性:

    int result;
    
    // Sort by A descending => invert the sign of the result
    result = - item1.A.CompareTo(item2.A);
    if (result != 0) return result;
    
    // then by B ascending
    result = item1.B.CompareTo(item2.B);
    if (result != 0) return result;
    
    // then by C ascending
    result = item1.C.CompareTo(item2.C);
    if (result != 0) return result;
    
    // then by D ascending
    result = item1.D.CompareTo(item2.D);
    if (result != 0) return result;
    
    // ... add other comparisons here if you ever need to in the future
    
    return result;
    

    【讨论】:

    • 是的。这更具可读性。但我认为 Rahul 只是指出了订单或运营商。然后我故意换成降序。因此,对于您的版本,它应该是 item2.A.CompareTo(item1.A)
    • “所以对于你的版本,它应该是 item2.A.CompareTo(item1.A)” - 是的,你是对的。我会更新它..
    【解决方案3】:

    是这样吗?

    return item2.A - item1.A;

    或者应该是

    return item1.A - item2.A;

    【讨论】:

    • 您提议的更改将导致 OP 的两个案例不再使用相同的比较(它将与第一行的 OrderBy 而不是 OrderByDescending 相同)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多