有没有简单的通用方法将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
感谢名单
答案 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 article或this forum thread。
如果在函数/过程中使用动态数组,请不要忘记使用显式const
或var
参数(如上所述),否则它将在每次调用时生成临时副本,因此它可能很慢。
答案 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);