有没有办法将2个数组合二为一?

时间:2011-08-18 05:39:41

标签: arrays delphi concatenation

有没有简单的通用方法将2个数组添加到一个?在下面的情况下,不可能简单地使用C := A + B语句...... 我想避免每次都为它制作algorhytm。

TPerson = record
    Birthday: Tdate;
    Name, Surname:string;
end;

Tpeople = array of TPerson;

var A, B, C:Tpeople;

C:=A+B; // it is not possible

感谢名单

5 个答案:

答案 0 :(得分:11)

由于每个string记录中有两个TPerson字段,因此您不能只使用二进制“移动”,因为您会混淆string的引用计数 - 尤其是一个多线程的环境。

你可以手动完成 - 这很快又很好:

TPerson = record
  Birthday: TDate;
  Name, Surname: string;
end;

TPeople = array of TPerson;

var A, B, C: TPeople;

// do C:=A+B
procedure Sum(const A,B: TPeople; var C: TPeople);
begin
var i, nA,nB: integer;
begin
  nA := length(A);
  nB := length(B);
  SetLength(C,nA+nB);
  for i := 0 to nA-1 do
    C[i] := A[i];
  for i := 0 to nB-1 do
    C[i+nA] := B[i];
end;

或者你可以使用我们的TDynArray包装器,它有一个处理这种情况的方法:

procedure AddToArray(var A: TPeople; const B: TPeople);
var DA: TDynArray;
begin
  DA.Init(TypeInfo(TPeople),A);
  DA.AddArray(B); // A := A+B
end;

AddArray方法可以添加原始数组的子端口:

/// add elements from a given dynamic array
// - the supplied source DynArray MUST be of the same exact type as the
// current used for this TDynArray
// - you can specify the start index and the number of items to take from
// the source dynamic array (leave as -1 to add till the end)
procedure AddArray(const DynArray; aStartIndex: integer=0; aCount: integer=-1);

请注意,对于此类记录,它将使用System._CopyRecord RTL函数,该函数未针对速度进行优化。我写了一个更快的版本 - 请参阅this blog articlethis forum thread

如果在函数/过程中使用动态数组,请不要忘记使用显式constvar参数(如上所述),否则它将在每次调用时生成临时副本,因此它可能很慢。

答案 1 :(得分:3)

没有任何内置功能可以连接动态数组。

您可以考虑使用Generics.Collections,TList中的一个通用容器类。

在你的情况下,你将有3个TList实例,比如A,B和C.然后你可以写

A.Clear;
A.AddRange(B);
A.AddRange(C);

我认为这与您开箱即用的内容一样接近您想要的内容。

如果您准备自己编写一些代码,那么您可以使用运算符重载来使用您需要的确切语法。声明包含具有私有可见性的TPerson数组的记录。然后,您需要实现Add运算符,Count属性和默认的Items []属性。这也可以是通用的,所以你只需要写一次。

TTurboArray = record<T>
private
  FItems: array of T;
  //property accessors here
 public
   class operator Add(a, b: TTurboArray<T>): TTurboArray<T>;
   property Count: Integer read GetCount write SetCount;
   property Items[Index: Integer]: T read GetItem write SetItem; default;
end;

您可以根据需要将此想法扩展为非常强大的数据结构。

答案 2 :(得分:1)

有一种快速而肮脏的方法可以做到这一点。这是一个可怕的黑客,但它应该工作,甚至照顾引用计数:

function ConcatPeople(const A, B: TPeople): TPeople;
var
  Temp: TPeople;
  ALen, BLen: Integer;
begin
  Result := Copy(A);
  BLen := Length(B);
  if BLen = 0 then
    Exit;
  ALen := Length(A);
  Temp := Copy(B);
  SetLength(Result, ALen + BLen);
  Move(Temp[0], Result[ALen], BLen * SizeOf(B[0]));
  FillChar(Temp[0], BLen * SizeOf(B[0]), 0);
end;

实际上,Temp中的数据与Result中的空记录“交换”,因此余额得以维持,并且引用计数将继续有效。

更新

对于它的价值:这与this accepted SO answer中使用的技术相同,例如, &的TList LT; T&GT; .Insert。我删除了这个答案,但我仍然认为它是有效的,所以我再次取消删除它。它可以在Move / FillChar块周围锁定,因此没有人在“交换”时访问这些项目。我会补充一点。

答案 3 :(得分:1)

以下是我处理它的方法,虽然它需要对原始代码进行一些修改(但希望对你来说无关紧要)才能使用TArray:

(在XE2中测试)

uses
  Generics.Collections;

type
  TArrayExt = class(TArray)
    class function Concat<T>(const First, Second: array of T): TArray<T>; overload;
  end;

class function TArrayExt.Concat<T>(const First, Second: array of T): TArray<T>;
var
  i: Integer;
  l: Integer;
begin
  l := Length(First);
  SetLength(Result, l + Length(Second));
  for i := Low(First) to High(First) do
    Result[i] := First[i];
  for i := Low(Second) to High(Second) do
    Result[l + i] := Second[i];
end;

type
  TPerson = record
    Birthday: TDate;
    Name, Surname: String;
  end;

  TPeople = TArray<TPerson>;

var
  A, B, C: TPeople;
begin
  C := TArrayExt.Concat<TPerson>(A, B);

这里的主要区别是我使用“TArray”而不是“TPerson阵列”。这可以用于数组字符串,记录等。我发现这样做的主要好处是它真正制作副本而不是移动。而且我使用的是“普通”的Delphi函数,而不是大容量内存副本,这可以给我带来的好处。

当然,如果您在紧密循环中执行此操作并且需要性能,这种方式可能不适合您。但我认为这对大多数其他情况来说是最好的,特别是在维护和可读性方面。

(除非有人发表关于这里有一些可怕的隐藏内存泄漏的评论。希望不要!)

答案 4 :(得分:0)

您的代码在最新版本的delphi C := A+B;中运行良好。

但是对于旧版本中的动态数组,您可以使用函数concat。例如:

C := Concat(A, B);