Delphi TStringList是否启用二进制搜索而不诉诸?
我正在从ADO查询构建一个stringlist,在查询中,返回排序结果并按顺序添加它们要快得多。这会给我一个已经排序的列表,然后调用Sort或设置sorted true会花费我很多时间,因为快速排序算法在已经排序的列表上执行得不好。有没有办法设置TStringList以使用二进制搜索而不运行排序?Delphi TStringList是否启用二进制搜索而不诉诸?,delphi,delphi-xe2,Delphi,Delphi Xe2,我正在从ADO查询构建一个stringlist,在查询中,返回排序结果并按顺序添加它们要快得多。这会给我一个已经排序的列表,然后调用Sort或设置sorted true会花费我很多时间,因为快速排序算法在已经排序的列表上执行得不好。有没有办法设置TStringList以使用二进制搜索而不运行排序? 在您询问之前,我无权访问CustomSort属性 假设StringList所需的排序顺序与AdoQuery的order BY相同,我不确定我是否理解您所担心的问题 当然,要做的事情是在StringLi
在您询问之前,我无权访问CustomSort属性 假设StringList所需的排序顺序与AdoQuery的order BY相同,我不确定我是否理解您所担心的问题 当然,要做的事情是在StringList仍然为空时将Sorted on the StringList设置为True,然后插入AdoQuery中的行。这样,当StringList将要添加一个条目时,它将使用IndexOf搜索该条目,IndexOf将使用Find(进行二进制搜索)来检查重复项。但以这种方式使用Add并不涉及快速排序,因为StringList已经将自身视为已排序 鉴于您的评论和您自己的答案,我通过NexusDB质量套件中的测线计时器分析器运行了下面的程序。结果是,尽管使用二进制搜索与
TStringList.IndexOf
在执行速度上存在明显差异,但它们与TStringList
的快速排序的使用(或不使用)无关。此外,我使用的二进制搜索方式与TStringList.Find
work中的二进制搜索方式之间的细微差异可以解释这种差异-请参见下面的更新#2
程序生成200K100个字符串,然后将它们插入到字符串列表中。StringList以两种方式生成,首先在添加任何字符串之前将Sorted设置为True,然后仅在添加字符串之后将Sorted设置为True<然后使用code>StringList.IndexOf和您的bin搜索
查找已添加的每个字符串。结果如下:
Line Total Time Source
80 procedure Test;
119 0.000549 begin
120 2922.105618 StringList := GetList(True);
121 2877.101652 TestIndexOf;
122 1062.461975 TestBinSearch;
123 29.299069 StringList.Free;
124
125 2970.756283 StringList := GetList(False);
126 2943.510851 TestIndexOf;
127 1044.146265 TestBinSearch;
128 31.440766 StringList.Free;
129 end;
130
131 begin
132 Test;
133 end.
function BinSearch(slList: TStringList; sToFind : String) : integer;
var
L, R, m : integer;
begin
L := 0;
R := slList.Count - 1;
if R < L then begin
Result := -1;
exit;
end;
m := (L + R) div 2;
while slList.Strings[m] <> sToFind do begin
m := (L + R) div 2;
if CompareText(slList.Strings[m], sToFind) < 0 then
L := m + 1
else
if CompareText(slList.Strings[m], sToFind) > 0 then
R := m - 1;
if L > R then
break;
end;
if slList.Strings[m] = sToFind then
Result := m
else
Result := -1;
end;
我遇到的问题是,您的BinSearch
实际上从未返回1
,失败次数等于搜索的字符串数。如果你能解决这个问题,我很乐意重新做测试
program SortedStringList2;
[...]
const
Rows = 200000;
StrLen = 100;
function ZeroPad(Number : Integer; Len : Integer) : String;
begin
Result := IntToStr(Number);
if Length(Result) < Len then
Result := StringOfChar('0', Len - Length(Result)) + Result;
end;
function GetList(SortWhenEmpty : Boolean) : TStringList;
var
i : Integer;
begin
Result := TStringList.Create;
if SortWhenEmpty then
Result.Sorted := True;
for i := 1 to Rows do
Result.Add(ZeroPad(i, StrLen));
if not SortWhenEmpty then
Result.Sorted := True;
end;
Function BinSearch(slList: TStringList; sToFind : String) : integer;
var
i, j, k : integer;
begin
try
i := slList.Count div 2;
k := i;
if i = 0 then
begin
Result := -1;
// SpendLog('BinSearch List Empty, Exiting...');
exit;
end;
while slList.Strings[i] <> sToFind do
begin
if CompareText(slList.Strings[i], sToFind) < 0 then
begin
j := i;
k := k div 2;
i := i + k;
if j=i then
break;
end else
if CompareText(slList.Strings[i], sToFind) > 0 then
begin
j := i;
k := k div 2;
i := i - k;
if j=i then
break;
end else
break;
end;
if slList.Strings[i] = sToFind then
result := i
else
Result := -1;
except
//SpendLog('<BinSearch> Exception: ' + ExceptionMessage + ' At Line: ' + Analysis.LastSourcePos);
end;
end;
procedure Test;
var
i : Integer;
StringList : TStringList;
procedure TestIndexOf;
var
i : Integer;
Index : Integer;
Failures : Integer;
S : String;
begin
Failures := 0;
for i := 1 to Rows do begin
S := ZeroPad(i, StrLen);
Index := StringList.IndexOf(S);
if Index < 0 then
Inc(Failures);
end;
Assert(Failures = 0);
end;
procedure TestBinSearch;
var
i : Integer;
Index : Integer;
Failures : Integer;
S : String;
begin
Failures := 0;
for i := 1 to Rows do begin
S := ZeroPad(i, StrLen);
Index := BinSearch(StringList, S);
if Index < 0 then
Inc(Failures);
end;
//Assert(Failures = 0);
end;
begin
StringList := GetList(True);
TestIndexOf;
TestBinSearch;
StringList.Free;
StringList := GetList(False);
TestIndexOf;
TestBinSearch;
StringList.Free;
end;
begin
Test;
end.
在这一点上,二进制搜索的性能明显优于TStringList.IndexOf
,与我的预期相反,在添加字符串之前或之后将TStringList.Sorted
设置为True并没有真正的区别
更新#2原来原因是
BinSearch
比TStringList快。IndexOf
纯粹是因为BinSearch
使用CompareText
而TStringList.IndexOf
使用AnsiCompareText
(通过.Find
)。如果我将BinSearch
更改为使用AnsiCompareText
,它将比TStringList.IndexOf
慢1.6倍 最后,我刚刚完成了一个二进制搜索,以像数组一样检查字符串列表:
Function BinSearch(slList: TStringList; sToFind : String) : integer;
var
i, j, k : integer;
begin
try
try
i := slList.Count div 2;
k := i;
if i = 0 then
begin
Result := -1;
SpendLog('BinSearch List Empty, Exiting...');
exit;
end;
while slList.Strings[i] <> sToFind do
begin
if CompareText(slList.Strings[i], sToFind) < 0 then
begin
j := i;
k := k div 2;
i := i + k;
if j=i then
break;
end else
if CompareText(slList.Strings[i], sToFind) > 0 then
begin
j := i;
k := k div 2;
i := i - k;
if j=i then
break;
end else
break;
end;
if slList.Strings[i] = sToFind then
result := i
else
Result := -1;
except
SpendLog('<BinSearch> Exception: ' + ExceptionMessage + ' At Line: ' + Analysis.LastSourcePos);
end;
finally
end;
end;
函数bin搜索(slList:TStringList;sToFind:String):整数;
变量
i、 j,k:整数;
开始
尝试
尝试
i:=slList.Count div 2;
k:=i;
如果i=0,那么
开始
结果:=-1;
SpendLog('bin搜索列表为空,正在退出…');
出口
结束;
而slList.Strings[i]sToFind do
开始
如果CompareText(slList.Strings[i],sToFind)<0,则
开始
j:=i;
k:=k第2部分;
i:=i+k;
如果j=i,那么
打破
结束其他
如果CompareText(slList.Strings[i],sToFind)>0,则
开始
j:=i;
k:=k第2部分;
i:=i-k;
如果j=i,那么
打破
结束其他
打破
结束;
如果slList.Strings[i]=sToFind,则
结果:=i
其他的
结果:=-1;
除了
SpendLog('Exception:'+ExceptionMessage+'位于第行:'+Analysis.LastSourcePos);
结束;
最后
结束;
结束;
如果需要的话,我稍后会清理这个问题。我打算建议使用一个interposer类直接更改FSorted字段,而不调用其setter方法,作为副作用,setter方法调用Sort方法。但是在Delphi2007中查看TStringList的实现时,我发现Find总是执行二进制搜索,而不检查排序属性。当然,如果列表项没有排序,这将失败,但在您的情况下,它们是排序的。因此,只要您记得调用Find而不是IndexOf,您就不需要做任何事情。
TStringLsit
速度很慢,我认为您最好使用TList
,它具有二进制搜索功能:另一个可能更快的选项是TDictionary
是,但是,以这种方式插入比插入到未排序的列表需要更长的时间,而且ADO查询order by的排序方式与stringlist的排序方式相同。使用我的200k测试数据库,插入到排序的列表需要约2000毫秒,而插入到未排序的列表需要约1500毫秒。插入排序正在消耗这额外的500毫秒为什么TStringList.Find
工作得非常好(如果列表已排序,则使用二进制搜索),并且二进制搜索仅在项目已排序的情况下才有意义。您使用CompareText
两次也不是很理想:只使用CompareText
一次,将结果保存在变量中,然后决定保存的值。@rudyvelthui这是正确的,但我需要先排序。这需要时间。如果我有权访问自定义排序id,请实现一种比排序列表上的快速排序更快的算法。我在测试您的BinSearch时发现一个错误,但它仍然会在200k中重复68000多次失败。我已经更新了我的答案,添加了对Wikipedia二进制搜索实现的测试,它返回0个失败。嗯,我尝试了这个,但Find似乎返回0或列表大小,而不是负结果,这可能会导致问题。如果找不到条目,函数将返回false。out参数Idx将是inde
Function BinSearch(slList: TStringList; sToFind : String) : integer;
var
i, j, k : integer;
begin
try
try
i := slList.Count div 2;
k := i;
if i = 0 then
begin
Result := -1;
SpendLog('BinSearch List Empty, Exiting...');
exit;
end;
while slList.Strings[i] <> sToFind do
begin
if CompareText(slList.Strings[i], sToFind) < 0 then
begin
j := i;
k := k div 2;
i := i + k;
if j=i then
break;
end else
if CompareText(slList.Strings[i], sToFind) > 0 then
begin
j := i;
k := k div 2;
i := i - k;
if j=i then
break;
end else
break;
end;
if slList.Strings[i] = sToFind then
result := i
else
Result := -1;
except
SpendLog('<BinSearch> Exception: ' + ExceptionMessage + ' At Line: ' + Analysis.LastSourcePos);
end;
finally
end;
end;