排序 TObjectList<T> 交换相等的值

Posted

技术标签:

【中文标题】排序 TObjectList<T> 交换相等的值【英文标题】:Sorting TObjectList<T> swaps equal values [duplicate] 【发布时间】:2022-01-20 22:49:23 【问题描述】:

我有以下(简化的)类定义:

  TMyObject = class
  private
    FDoubleValue: Double;
    FText: string;
  protected
  public
    constructor Create(ADoubleValue: Double; AText: string);
    property DoubleValue: Double read FDoubleValue write FDoubleValue;
    property Text: string read FText write FText;
  end;

以下示例代码显示了我如何对TObjectList&lt;TMyObject&gt; (FMyObjects) 进行排序并将它们显示在TListBox 中。

constructor TfrmMain.Create(AOwner: TComponent);
begin
  inherited;
  FMyObjects := TObjectList<TMyObject>.Create;
  FMyObjects.OwnsObjects := true; // Default but for clarity
end;

destructor TfrmMain.Destroy;
begin
  FMyObjects.Free;
  inherited;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
var
  ii: Integer;
begin
  FMyObjects.Add(TMyObject.Create(100.00, 'Item 1'));
  FMyObjects.Add(TMyObject.Create(200.00, 'Item 2'));
  FMyObjects.Add(TMyObject.Create(300.00, 'Item 3')); // Duplicate sort value
  FMyObjects.Add(TMyObject.Create(300.00, 'Item 4')); // Duplicate sort value
  FMyObjects.Add(TMyObject.Create(400.00, 'Item 5'));
  ObjectsToListBox;
end;

procedure TfrmMain.SortList;
var
  Comparer: IComparer<TMyObject>;
begin
  Comparer := TDelegatedComparer<TMyObject>.Create(
    function(const MyObject1, MyObject2: TMyObject): Integer
    begin
      result := CompareValue(MyObject1.DoubleValue, MyObject2.DoubleValue, 0);
    end);
  FMyObjects.Sort(Comparer);
end;

procedure TfrmMain.ObjectsToListBox;
var
  ii: Integer;
begin
  ListBox1.Items.Clear;
  for ii := 0 to FMyObjects.Count - 1 do
    ListBox1.Items.Add(Format('%d - %.1f - %s', [ii, FMyObjects[ii].DoubleValue, 
       FMyObjects[ii].Text]));
end;

procedure TfrmMain.Button1Click(Sender: TObject);
begin
  SortList;
  ObjectsToListBox;
end;

每次单击Button1(并对列表进行排序),FMyObjects[2](Item3)和FMyObjects[3]('Item4')在列表中交换位置。在我的“真实世界”(绘图)应用程序中,这是不可取的。

我还在 CompareValue 函数调用中尝试了 Epsilon 的不同值以及匿名函数的不同实现(比较值并返回 1、-1 或 0),但似乎都没有什么不同。

我是否遗漏了某些东西(例如控制此行为的属性)或者这是“设计使然”并且无法避免?

【问题讨论】:

这是因为 Delphi 排序实现者不稳定。这对于许多应用程序来说都很好,而且是设计使然。这在之前已经讨论过很多次了。在此处搜索稳定排序。 spring4d 中的 TimSort 实现是一个稳定的排序。这是我们添加它的主要原因。 【参考方案1】:

这是设计使然。内部使用的快速排序实现不是一个稳定的实现,因此需要重新排序相等的项目。为了使排序稳定,您需要扩展比较器以考虑到这一点。 F.I.当 DoubleValue 属性相等时,您可以比较 Text 属性。

【讨论】:

当今软件的一个常见问题:只能对一列进行排序,而不是能够链接多列。如果前一列的比较结果相等,我通常对每一列使用Array of Byte 来排序每个字节代表另一列索引的位置。 @AmigoJack 您始终可以让您的 Comparer 函数接受两个对象/记录,而不仅仅是来自这些对象的单个参数,然后在您的 Comaprer 方法中以尽可能多的杠杆和所需的任何顺序比较单个参数. @SilverWarior 我这样做。我说的还有很多其他人没有。 那个解决方案是贴膏药。到目前为止,更好的方法是使用稳定的排序

以上是关于排序 TObjectList<T> 交换相等的值的主要内容,如果未能解决你的问题,请参考以下文章

java 集合交并补

半平面交

第k小数(桶排序)

第k小数(桶排序)

访问 TObjectList 线程的不同索引是不是安全?

TList TObjectList的区别和使用