簡體   English   中英

為什么使用Linq-to-Objects進行排序將商品與其自身進行比較?

[英]Why does ordering with Linq-to-Objects compare items to themselves?

考慮以下使用LINQ OrderByThenBy簡單代碼:

static void Main()
{
  var arr1 = new[] { "Alpha", "Bravo", "Charlie", };

  var coStr = Comparer<string>.Create((x, y) =>
  {
    Console.WriteLine($"Strings: {x} versus {y}");
    return string.CompareOrdinal(x, y);
  });

  arr1.OrderBy(x => x, coStr).ToList();

  Console.WriteLine("--");

  var arr2 = new[]
  {
    new { P = "Alpha", Q = 7, },
    new { P = "Bravo", Q = 9, },
    new { P = "Charlie", Q = 13, },
  };

  var coInt = Comparer<int>.Create((x, y) =>
  {
    Console.WriteLine($"Ints: {x} versus {y}");
    return x.CompareTo(y);
  });

  arr2.OrderBy(x => x.P, coStr).ThenBy(x => x.Q, coInt).ToList();
}

這只是使用一些比較器,將比較的內容寫到控制台。

在我的框架的硬件和版本(.NET 4.6.2)上,輸出如下:

Strings: Bravo versus Alpha
Strings: Bravo versus Bravo
Strings: Bravo versus Charlie
Strings: Bravo versus Bravo
--
Strings: Bravo versus Alpha
Strings: Bravo versus Bravo
Ints: 9 versus 9
Strings: Bravo versus Charlie
Strings: Bravo versus Bravo
Ints: 9 versus 9

我的問題是: 為什么他們將查詢中的項目與其自身進行比較?

在第一種情況下,在--分隔符之前,它們進行了四個比較。 其中兩個將條目與其自身進行比較(“字符串:Bravo與Bravo”)。 為什么?

在第二種情況下,永遠都不需要訴諸於Q特性(整數)。 因為在P值中沒有重復項(按順序比較),所以永遠不需要從ThenBy搶七。 我們仍然兩次看到“整數:9對9”。 為什么要使用具有相同參數的ThenBy比較器?

注意:任何比較器都必須在與自己進行比較時返回0 因此,除非該算法只想檢查我們是否正確實現了一個比較器(無論如何它永遠無法完全完成),那是怎么回事?

請注意:在我的示例中,查詢產生的元素中沒有重復項。

我在另一個示例中看到了相同的問題,該示例從查詢中產生了更多的條目。 以上我僅舉一個小例子。 這種情況也會產生偶數個元素。

OrderBy使用的QuickSort方法的參考源中,您可以看到以下兩行:

while (i < map.Length && CompareKeys(x, map[i]) > 0) i++;
while (j >= 0 && CompareKeys(x, map[j]) < 0) j--;

這些while循環將一直運行,直到它們找到的元素不再比“ x指向的元素“更大”(分別為“更少”)。 因此,當比較相同的元素時,它們將斷開。

我無法證明它是數學的,但是我想避免比較相同的元素會使算法更加復雜,並且引入的開銷比單次比較更會影響性能。
(請注意,您的比較器應足夠聰明地實現,以針對相同的元素快速返回0

在第一種情況下,在-分隔符之前,它們進行了四個比較。 其中兩個將條目與其自身進行比較(“字符串:Bravo與Bravo”)。 為什么?

效率。 當然,可以檢查對象本身不是首先,但是這意味着對每個比較都進行額外的操作,以避免出現相對較少且在大多數情況下非常便宜的情況(大多數比較器都是這樣)。 那將是凈虧損。

(順便說一句,我確實對算法進行了這樣的更改進行了實驗,在進行測量時,使用常規比較(例如默認int比較器)確實確實是效率下降)。

在第二種情況下,永遠都不需要訴諸於Q特性(整數)。 因為在P值中沒有重復項(按順序比較),所以永遠不需要從ThenBy搶七。 我們仍然兩次看到“整數:9對9”。 為什么要使用具有相同參數的ThenBy比較器?

誰說沒有重復? 內部比較有兩件事(不一定是引用類型,因此對引用標識的短路並非總是一種選擇),並且要遵循兩個規則。 第一條規則需要平手,所以平局就完成了。

該代碼旨在用於第一次比較可能具有相等值的情況。

如果知道OrderBy不會有相等的值,那么這是一個知道不要使用不必要的ThenBy的人,因為他們是可能知道這一點的人。

好的,讓我們在這里看看可能性:

  1. T是一個值類型

    為了檢查是否將一個項目與其自身進行比較,它首先需要檢查兩個項目是否相同。 你會怎么做?

    如果項目不相同,則可以先調用Equals ,然后再調用CompareTo 您真的要這么做嗎? Equals的成本將與比較的成本大致相同,因此您實際上將使訂購成本加倍的原因是什么? OrderBy只是比較所有項目,期間。

  2. T是參考類型

    c#不允許您僅使用通用約束進行重載,因此您需要在運行時檢查T是否為引用類型,然后調用將改變上述行為的特定實現。 您是否要在每種情況下都承擔該費用? 當然不是。

    如果比較昂貴,則在比較邏輯中實現參考優化,以避免在將項目與其自身進行比較時產生愚蠢的成本,但是該選擇必須由您自己決定。 我很確定string.CompareTo正是這樣做的。

我希望這可以使我的答案更清楚,對以前的簡短回答感到抱歉,我的推理沒有那么明顯。

簡單來說,情況1

var coStr = Comparer<string>.Create((x, y) =>
{
    Console.WriteLine($"Strings: {x} versus {y}");
    return string.CompareOrdinal(x, y);
});

我們只是在比較元素,如果結果為0,則沒有條件可以忽略。因此,Console.WriteLine條件與比較的輸出無關。 如果您像下面那樣更改代碼

var coStr = Comparer<string>.Create((x, y) =>
{
   if (x != y)
      Console.WriteLine($"Strings: {x} versus {y}");
   return string.CompareOrdinal(x, y);
});

您的輸出將像

Strings: Bravo versus Alpha
Strings: Bravo versus Charlie

對於第二條語句,我們正在檢查兩者的輸出是否相同,因此對於字符串比較,將返回0,然后進行比較,因此將其取為1並輸出所需的值。 希望它清除您的疑慮:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM