【问题标题】:Fast copy of TList <T>?TList <T> 的快速复制?
【发布时间】:2012-03-17 18:09:22
【问题描述】:

有没有快速复制通用 TList 的方法?

Copy.Capacity := List.Count;
for Item in List do
  Copy.Add (Item);

很慢。似乎没有办法使用CopyMemory,因为我无法获得内部数组的内存地址(从信息隐藏的角度来看,这很明显)。我想念类似的东西

List.Copy (Copy);

它使用内部表示的知识来提高性能。能做到吗?

【问题讨论】:

  • TList&lt;T&gt;.AddRange() 有 3 个重载版本。其中只有一个使用 TArray 作为参数
  • @teran:你是对的。但事实证明AddRange 的实现并不快。我从问题中删除了那个令人困惑的句子。
  • 又出现了非建设性的否决者……请您至少发表评论吗?
  • +1 来自支持者(:这个问题非常合理!

标签: delphi collections delphi-xe


【解决方案1】:

对于通用TList&lt;T&gt;,根本不可能实现您想要的功能。那是因为复制T 的内容可能涉及的不仅仅是简单的内存复制。如果T 包含任何托管类型(即字符串、接口等),则必须增加这些托管对象的引用计数。

  • 如果您的 T 确实包含托管类型,那么我怀疑您是否可以比现有代码做得更好。
  • 如果您的 T 不包含任何托管类型,则内存副本是可行的,但您需要创建自己的类来封装此列表,因为 TList&lt;T&gt; 不合适。

【讨论】:

    【解决方案2】:

    你总是可以重新解释记忆。

    type
      TListHack<T> = class(TEnumerable<T>)
      private
        FItems: array of T;
        FCount: Integer;
      public
        class procedure FastAdd(Source, Dest: TList<T>);
      end;
    
    { TListHack<T> }
    
    class procedure TListHack<T>.FastAdd(Source, Dest: TList<T>);
    var
      SourceHack: TListHack<T>;
      DestHack: TListHack<T>;
      TI: PTypeInfo;
    begin
      TI := TypeInfo(T);
      if not (TI.Kind in [tkInteger, tkChar, tkFloat,
        tkSet, tkClass, tkMethod, tkWChar, tkInt64, tkClassRef, tkPointer, tkProcedure]) then
        raise Exception.CreateFmt('Type %s is not supported', [TI.Name]);
    
      if Source.Count = 0 then
        Exit;
      DestHack := TListHack<T>(Dest);
      SourceHack := TListHack<T>(Source);
      if Dest.Capacity < Dest.Count + Source.Count then
        Dest.Capacity := Dest.Count + Source.Count;
    
      Move(SourceHack.FItems[0], DestHack.FItems[Dest.Count], Source.Count * SizeOf(T));
      DestHack.FCount := DestHack.FCount + Source.Count;
    end;
    
    procedure TForm6.FormCreate(Sender: TObject);
    var
      Source, Dest: TList<Integer>;
      Arr: TArray<Integer>;
    begin
      Source := TList<Integer>.Create;
      Dest := TList<Integer>.Create;
      try
        Source.Add(1); Source.Add(2); Source.Add(3);
        Dest.Add(10);
        TListHack<Integer>.FastAdd(Source, Dest);
        Assert(Dest.Count = 4);
        ShowMessageFmt('%d, %d, %d, %d', [Dest[0], Dest[1], Dest[2], Dest[3]]);
      finally
        Source.Free; Dest.Free;
      end;
    
      TListHack<IUnknown>.FastAdd(TList<IUnknown>.Create, TLIst<IUnknown>.Create); // exception
    end;
    

    但是很危险

    【讨论】:

    • 是的,这确实非常危险。 TList &lt;T&gt; 的对象布局的任何更改都会破坏它 - 除了 David 提到的托管类型之外。我想这对我来说是没有选择的。
    • 一些非常流行的组件(例如 VirtualTree)使用这个 hack 来解决 VCL 错误。您决定..您可以使用标准 TList.Add 用于托管类型,FastCopy 用于 Integer/Double 等。
    • 你也可以像这样写自己的列表类。
    • 是的,我认为具有通用约束的自定义列表类会好得多。
    • @Smasher 我认为你不能充分限制泛型类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-04
    • 1970-01-01
    • 2019-11-26
    相关资源
    最近更新 更多