你可以声明一个辅助类来避免比较的左右两侧必须是特定类型的
IComparer
的要求,例如:
type
TLeftComparison<T> = reference to function(const Left: T; var Value): Integer;
TListHelper<T> = class
public
class function BinarySearch(Instance: TList<T>; var Value; out FoundIndex: Integer;
Comparison: TLeftComparison<T>; Index, Count: Integer): Boolean; overload;
class function BinarySearch(Instance: TList<T>; var Value; out FoundIndex: Integer;
Comparison: TLeftComparison<T>): Boolean; overload;
class function Contains(Instance: TList<T>; var Value; Comparison: TLeftComparison<T>): Boolean;
class function IndexOf(Instance: TList<T>; var Value; Comparison: TLeftComparison<T>): Integer;
class function LastIndexOf(Instance: TList<T>; var Value; Comparison: TLeftComparison<T>): Integer;
end;
class function TListHelper<T>.BinarySearch(Instance: TList<T>; var Value; out FoundIndex: Integer;
Comparison: TLeftComparison<T>; Index, Count: Integer): Boolean;
var
L, H: Integer;
mid, cmp: Integer;
begin
Result := False;
L := Index;
H := Index + Count - 1;
while L <= H do
begin
mid := L + (H - L) shr 1;
cmp := Comparison(Instance[mid], Value);
if cmp < 0 then
L := mid + 1
else
begin
H := mid - 1;
if cmp = 0 then
Result := True;
end;
end;
FoundIndex := L;
end;
class function TListHelper<T>.BinarySearch(Instance: TList<T>; var Value; out FoundIndex: Integer;
Comparison: TLeftComparison<T>): Boolean;
begin
Result := BinarySearch(Instance, Value, FoundIndex, Comparison, 0, Instance.Count);
end;
class function TListHelper<T>.Contains(Instance: TList<T>; var Value; Comparison: TLeftComparison<T>): Boolean;
begin
Result := IndexOf(Instance, Value, Comparison) >= 0;
end;
class function TListHelper<T>.IndexOf(Instance: TList<T>; var Value; Comparison: TLeftComparison<T>): Integer;
var
I: Integer;
begin
for I := 0 to Instance.Count - 1 do
if Comparison(Instance[I], Value) = 0 then
Exit(I);
Result := -1;
end;
class function TListHelper<T>.LastIndexOf(Instance: TList<T>; var Value; Comparison: TLeftComparison<T>): Integer;
var
I: Integer;
begin
for I := Instance.Count - 1 downto 0 do
if Comparison(Instance[I], Value) = 0 then
Exit(I);
Result := -1;
end;
然后你可以像这样使用它:
function CompareEstr(const Left, Right: TEstr): Integer;
begin
if Left.Date < Right.Date then
Exit(-1);
if Left.Date > Right.Date then
Exit(1);
Result := 0;
end;
function CompareEstr2(const Left: TEstr; var Value): Integer;
begin
if Left.Date < TDateTime(Value) then
Exit(-1);
if Left.Date > TDateTime(Value) then
Exit(1);
Result := 0;
end;
procedure Main;
var
Date: TDate;
Comparer: IComparer<TEstr>;
List: TEstrList;
Item: TEstr;
Index: Integer;
I: Integer;
begin
Comparer := nil;
List := nil;
try
Comparer := TComparer<TEstr>.Construct(CompareEstr);
List := TEstrList.Create(Comparer);
Date := EncodeDate(2011, 1, 1);
for I := 0 to 35 do
begin
Item.Date := IncMonth(Date, I);
List.Add(Item);
end;
List.Sort;
Date := EncodeDate(2011, 11, 1);
Item.Date := Date;
Index := List.IndexOf(Item);
Writeln(Format('TList.IndexOf(%s): %d', [DateToStr(Date), Index]));
List.BinarySearch(Item, Index);
Writeln(Format('TList.BinarySearch(%s): %d', [DateToStr(Date), Index]));
Writeln;
Index := TListHelper<TEstr>.IndexOf(List, Date, CompareEstr2);
Writeln(Format('TListHelper.IndexOf(%s): %d', [DateToStr(Date), Index]));
TListHelper<TEstr>.BinarySearch(List, Date, Index, CompareEstr2);
Writeln(Format('TListHelper.BinarySearch(%s): %d', [DateToStr(Date), Index]));
Readln;
finally
List.Free;
end;
end;
这当然不太类型安全(由于未经过类型定义的右侧比较参数),但需要允许对不同类型的值进行通用比较。如果小心处理,这应该不是问题。否则,您也可以为大多数需要比较的常用类型编写重载版本。
TDictionary<Date,Integer>
来使搜索更快。 - Cosmin PrundTEstr
、TExtr
、TExtrList
和TEstrList
有些混淆(它们在代码和解释上有所不同)。如果您能编辑这些内容,那就太好了。 - Heinrich Ulbricht