如何在Delphi中合并2个字符串数组

时间:2011-06-01 06:47:53

标签: arrays delphi

我有2个或更多的动态字符串数组,填充了一些巨大的数据,我想将这个2数组合并到一个数组,我知道我可以用这样的for循环来做:

var
  Arr1, Arr2, MergedArr: Array of string;
  I: Integer;
begin
  // Arr1:= 5000000 records
  // Arr2:= 5000000 records

  // Fill MergedArr by Arr1
  MergedArr:= Arr1;

  // Set length of MergedArr to length of ( Arra1 + Arr2 )+ 2
  SetLength(MergedArr, High(Arr1)+ High(Arr2)+2);

  // Add Arr2 to MergedArr
  for I := Low(Arr2)+1 to High(Arr2)+1 do
    MergedArr[High(Arr1)+ i]:= Arr2[i-1];
end;

但是对于庞大的数据来说速度很慢,复制阵列内存数据有更快的方法吗?

3 个答案:

答案 0 :(得分:8)

首先string是特殊的,所以应该特别对待它:不要试图超越编译器,保持代码不变。字符串是特殊的,因为它的引用计数。每次将字符串从一个地方复制到另一个地方时,它的引用计数会递增。当引用计数达到0时,字符串将被销毁。你的代码很好用,因为它让编译器知道你在做什么,反过来编译器有机会正确地增加所有引用计数。

当然,你可以按照gabr答案的评论中的建议玩各种技巧,比如用零填充旧数组,这样新数组中的引用计数仍然有效,但如果你确实需要,你就不能这样做旧阵列也是如此。这有点像黑客(虽然可能在可预见的未来有效)。 (并且需要注意的是,我实际上喜欢这个黑客)。

无论如何,这是我答案的重要部分,你的代码很可能在将字符串从一个数组复制到另一个数组时速度慢,而且很可能在其他地方慢慢地。这是一个简短的控制台应用程序,它创建两个数组,每个数组有5M随机字符串,然后将两个数组合并为第三个,并显示创建合并所花费的时间。 合并在我的计算机上只需要大约300毫秒。填充阵列需要更长的时间,但我没有计时:

程序Project26;

{$APPTYPE CONSOLE}

uses SysUtils, Windows;

var a, b, c: array of string;
    i: Integer;

    Freq: Int64;
    Start, Stop: Int64;
    Ticks: Cardinal;

const count = 5000000;

begin
  SetLength(a,count);
  SetLength(b,count);
  for i:=0 to count-1 do
  begin
    a[i] := IntToStr(Random(1));
    b[i] := IntToStr(Random(1));
  end;

  WriteLn('Moving');

  QueryPerformanceFrequency(Freq);
  QueryPerformanceCounter(Start);

  SetLength(c, Length(a) + Length(b));
  for i:=0 to High(a) do
    c[i] := a[i];
  for i:=0 to High(b) do
    c[i+Length(a)] := b[i];

  QueryPerformanceCounter(Stop);
  WriteLn((Stop - Start) div (Freq div 1000), ' milliseconds');
  ReadLn;

end.

答案 1 :(得分:7)

您可以使用内置的Move功能将内存块移动到另一个位置。参数是源和目标内存块以及要移动的数据大小。

因为要复制字符串,所以必须在合并后通过用零填充来销毁源数组。否则,对于字符串的重新计算将是错误的,从而导致程序中的破坏和破坏。

var
  Arr1, Arr2, MergedArr: Array of string;
  I: Integer;
begin
  SetLength(Arr1, 5000000);
  for I := Low(Arr1) to High(Arr1) do
    Arr1[I] := IntToStr(I);

  SetLength(Arr2, 5000000);
  for I := Low(Arr2) to High(Arr2) do
    Arr2[I] := IntToStr(I);

  // Set length of MergedArr to length of ( Arra1 + Arr2 )+ 2
  SetLength(MergedArr, High(Arr1)+ High(Arr2)+2);

  // Add Arr1 to MergedArr
  Move(Arr1[Low(Arr1)], MergedArr[Low(MergedArr)], Length(Arr1)*SizeOf(Arr1[0]));

  // Add Arr2 to MergedArr
  Move(Arr2[Low(Arr2)], MergedArr[High(Arr1)+1], Length(Arr2)*SizeOf(Arr2[0]));

  // Cleanup Arr1 and Arr2 without touching string refcount.
  FillChar(Arr1[Low(Arr1)], Length(Arr1)*SizeOf(Arr1[0]), 0);
  FillChar(Arr2[Low(Arr2)], Length(Arr2)*SizeOf(Arr2[0]), 0);

  // Test
  for I := Low(Arr1) to High(Arr1) do begin
    Assert(MergedArr[I] = IntToStr(I));
    Assert(MergedArr[I] = MergedArr[Length(Arr1) + I]);
  end;

  // Clear the array to see if something is wrong with refcounts
  for I := Low(MergedArr) to High(MergedArr) do
    MergedArr[I] := '';
end;

答案 2 :(得分:3)

一个优秀的格言是最快的代码是永不运行的代码。由于复制费用昂贵,因此您应该避免复制费用。

您可以使用虚拟阵列执行此操作。创建一个包含array of string数组的类。在您的示例中,外部数组将包含两个字符串数组。

  • 添加Count属性,该属性返回所有数组中的字符串总数。
  • 添加一个默认的索引属性,该属性通过计算索引所引用的外部数组,然后从内部数组返回适当的值来进行操作。
  • 要获得额外分数,请实施一个枚举器以便在工作中使用。