Enumerable.OrderBy 方法是否保留具有相同排序(包括转换类型)的项目的插入顺序?

Does Enumerable.OrderBy Method preserve insertion order for items with same sorting - including conversion type?

我找到了 this documentation (Enumerable.OrderBy(Of TSource, TKey) Method (IEnumerable(Of TSource), Func(Of TSource, TKey))),其中写着:

This method performs a stable sort; that is, if the keys of two elements are equal, the order of the elements is preserved. In contrast, an unstable sort does not preserve the order of elements that have the same key.

但是,我不知道 stableunstable sort 之间的区别 (?).

假设我有一个 public 属性,一个名为 fields (as clsField()) 的数组,其中 parser 放置了所有对象...

Public Class clsField
    Public idx As String
    Public name As String
    Public order As Long
End Class

Public Class Container
    Public fields As clsField()

    Public Function getFields() As Dictionary(Of String, clsField)
        If (fields Is Nothing) OrElse (fields.Count < 1) Then Return New Dictionary(Of String, clsField)
        Return fields.OrderBy(Function(fld) fld.order).ToDictionary(Of String, clsField)(Function(fld) fld.idx, Function(fld) fld)
    End Function

    Public Function getFields(blnAlternative As Boolean) As Dictionary(Of String, clsField)
        If (fields Is Nothing) OrElse (fields.Count < 1) Then Return New Dictionary(Of String, clsField)
        Return fields.ToDictionary(Of String, clsField)(Function(fld) fld.idx, Function(fld) fld).OrderBy(Function(pair) pair.Value.order)
    End Function
End Class

Public Class Consumer
    Public cont As Container
    Public parse As Parser

    Public Sub New(strFormat)
        cont = New Container
        cont.fields = parse.deserializeOject(Of clsField())(strFormat)
    End Sub

    Public Sub printFields_Sort_Dic()
        For Each fld As clsField In cont.getFields().Values
            Console.WriteLine(fld.ToString)
        Next
    End Sub

    Public Sub printFields_Dic_Sort()
        For Each fld As clsField In cont.getFields(True).Values
            Console.WriteLine(fld.ToString)
        Next
    End Sub

    Public Sub Main()
        printFields_Dic_Sort()
        printFields_Sort_Dic()
    End Sub
End Class

与上述代码相关的两个疑惑:

  1. 对于具有相同 order 的元素,是否会保留 Parser 将元素插入数组 fields 的顺序?
  2. printFields_Dic_Sort() 的顺序是否与 printFields_Sort_Dic() 不同? (第一次转换然后排序;第二次排序然后转换为Dictionary

无法加入关于此的权威信息... 谢谢

已编辑

添加了返回的类型 Dictionary

好的,这是问题所在:

  1. 如果我想要 KeyValuePair (idx, obj) 不能使用 array ( 错误 : 这是可能的;挑战在于如何在数组之间转换)
  2. 我无法将排序后的 array 存储在 Dictionary 中,因为不能保证遍历它会得到有序的 values (right:不保证通用词典,见下文)。
  3. 可以选择使用 SortedDictionary。但是,没有从 arraySortedDictionary 的直接转换(除非你进行迭代)...(**对*?*:array/[=34 之间没有简单的转换=] 和 SortedDictionary)
  4. 那么,为了简单快捷的解决方案,它只保留使用 list。但是,也有问题,因为它应该是 KeyValuePair 的列表,我不能通过使用 toList 直接从 array 获取( 错误:可以得到List (Of KeyValuePair (Of ...)),但是需要事先转换)。

所以根本没有优雅(简短)的解决方案...(正确:无评论)

任何帮助将不胜感激(仍在使用它)- 在下面解决...

编辑二

好的,这就是我最终所做的:通过使用 array.ConvertAll (msdn) 并定义我自己的 Converter 函数来解决第 4 点的问题...将array 然后,通过 ToList 从已经将类型转换为 (Of KeyValuePair (Of String, clsFld)) 的中间 array 得到一个 List。希望对其他人有帮助:

'Imports System.Linq.Enumerable
'Imports System.Runtime.CompilerServices ' for extensions

Public Class clsField
    Public idx As String
    Public name As String
    Public weight As Long

    Public Sub New(Optional i As String = vbNullString, Optional n As String = vbNullString, Optional w As Long = vbNullString)
        idx = i : name = n : weight = w
    End Sub
End Class

Public Class Container
    Public fields As clsField()

    ' returns a list sorted by clsField.weight preserving order for elements with same 'weight' value
    Public Function getFields() As List(Of KeyValuePair(Of String, clsField))
        Dim auxList As List(Of KeyValuePair(Of String, clsField))

        If (fields Is Nothing) OrElse (fields.Count < 1) Then Return New List(Of KeyValuePair(Of String, clsField))
        ' .ToList to transform IEnumerable to the return type
        auxList = Array.ConvertAll(fields, New Converter(Of clsField, KeyValuePair(Of String, clsField))(AddressOf FieldToPair)).ToList
        Return auxList.OrderBy(Function(x) x.Value.weight).ToList()
    End Function

    Public Shared Function FieldToPair(fld As clsField) As KeyValuePair(Of String, clsField)
        Return New KeyValuePair(Of String, clsField)(fld.idx, fld)
    End Function
End Class

Public Class Consumer
    Public cont As Container

    Public Sub New()
        cont = New Container
        cont.fields.Add(New clsField("ffq", "foo30004", 33))
        cont.fields.Add(New clsField("ffc", "foo9997", 55))
        cont.fields.Add(New clsField("ffp", "foo9908", 55))
        cont.fields.Add(New clsField("ffo", "foo100001", 22))
        cont.fields.Add(New clsField("ffx", "foo8885", 33))
        cont.fields.Add(New clsField("ffz", "foo70002", 22))
        cont.fields.Add(New clsField("ffy", "foo8806", 33))
        cont.fields.Add(New clsField("ffa", "foo9009", 55))
        cont.fields.Add(New clsField("ffb", "foo8000", 55))
        cont.fields.Add(New clsField("ffn", "foo7003", 22))
    End Sub

    Public Sub printSortedFields()
        Dim aux As List(Of KeyValuePair(Of String, clsField))

        aux = cont.getFields()

        For Each pair As KeyValuePair(Of String, clsField) In aux
            Console.WriteLine(pair.Value.name)
            With pair.Value
                Debug.Print("name: " & .name & " || idx: " & .idx & " || weight: " & .weight)
            End With
        Next
    End Sub

    Public Sub Main()
        printSortedFields()
    End Sub
End Class

Module ArrayExtension ' custom method for array
    <Extension()>
    Public Sub Add(Of T)(ByRef arr As T(), item As T)
        If arr IsNot Nothing Then
            Array.Resize(arr, arr.Length + 1)
            arr(arr.Length - 1) = item
        Else
            ReDim arr(0)
            arr(0) = item
        End If

    End Sub
End Module

上面测试的结果

name: foo100001 || idx: ffo || weight: 22
name: foo70002 || idx: ffz || weight: 22
name: foo7003 || idx: ffn || weight: 22
name: foo30004 || idx: ffq || weight: 33
name: foo8885 || idx: ffx || weight: 33
name: foo8806 || idx: ffy || weight: 33
name: foo9997 || idx: ffc || weight: 55
name: foo9908 || idx: ffp || weight: 55
name: foo9009 || idx: ffa || weight: 55
name: foo8000 || idx: ffb || weight: 55
  1. 稳定排序保留了它们具有相同值的元素的顺序,所以是的,它们将return按照添加它们的相同顺序进行编辑。但这并不重要,因为您稍后会将它们推送到字典中。见下文。

  2. Dictionary<TKey, TValue> 没有定义顺序,可能不会保留元素 l 的顺序。因此 printFields_Sort_Dic() 可能 return 它们的顺序不同。