Delphi泛型动态数组的扩展--转贴

此文章转载于http://www.raysoftware.cn/?p=278&tdsourcetag=s_pcqq_aiomsg的博客

 

从Delphi支持泛型的第一天起就有了一种新的动态数组类型,泛型化的动态数组–TArray.
虽然这个类型比较方便,但是却没有提供更丰富的操作.因为XE4中提供了对数据类型的Helper扩展,例如StringHelper,企图实现一个TArrayHelper但是发现Helper不支持泛型的类型.
没办法只好包装了一个record,好处是似乎只要支持泛型的Delphi版本都可以支持.使用Demo如下:

procedure TestTArrayEx;
var
  a, b, c: TArrayEx<Integer>;
  f: TArrayEx<Single>;
  s : TArrayEx<string>;
  //
  i: Integer;
  z: array of Integer;
  args:TArrayEx<string>;
begin
  // assign

  a := [2, 2, 3, 4];
  s := ['haha', 'hello'];

  // clone
  b := a.Clone;

  // 给元素赋值
  b[0] := 5;

  // operator +
  c := a + b;

  c := 88 + c + 99;
  a.Append([10, 20, 30]);
  a.Insert(2, 1000);
  a.Insert(2, [1000, 45]);

  a[0] := 7;

  // Unique
  c.Unique;
  // Delete
  c.Delete(0, 3);

  // compare
  if c = z then
  // for in loop
  for i in c do
  if i = 0 then
  begin
  //
  end;

  //
  f := [1, 2, 3.1415926];
  f.Size := 200;
  if f[0] = 1.2 then
  begin
    f.Size := 100;
  end;
end;

这些成员方法和操作符重载都是原来的TArray所不具备的.注重开发效率可以用这个办法封装一些类型,简化操作.
TArrayEx的实现代码在下面.记得必须是支持泛型版本的Delphi哦,也就是至少要Delphi2009版以后的.

unit TJArrayEx;

{ ******************************************************************************
  泛型动态数组的扩展.

  sample:

  var
  a, b, c: TArrayEx<Integer>;
  f: TArrayEx<Single>;
  s : TArrayEx<string>;
  //
  i: Integer;
  z: array of Integer;
  args:TArrayEx<string>;
  begin
  // assign

  a := [2, 2, 3, 4];
  s := ['haha', 'hello'];

  // clone
  b := a.Clone;

  // 给元素赋值
  b[0] := 5;

  // operator +
  c := a + b;

  c := 88 + c + 99;
  a.Append([10, 20, 30]);
  a.Insert(2, 1000);
  a.Insert(2, [1000, 45]);

  a[0] := 7;

  // Unique
  c.Unique;
  // Delete
  c.Delete(0, 3);

  // compare
  if c = z then
  // for in loop
  for i in c do
  if i = 0 then
  begin
  //
  end;

  //

  f := [1, 2, 3.1415926];
  f.Size := 200;
  if f[0] = 1.0 then
  begin
  end;



  args := ['38inch','45inch','XL','XL2','X','38inch','45inch'];
  args.Unique;
  //sort
  args.Sort;
  //search
  if args.BinarySearch('XL',i) then
  ShowMessageFmt('foud index:%d',[i]);



  end;

  ****************************************************************************** }
interface

uses System.Generics.Defaults, System.SysUtils;

type
  TArrayEx<T> = record
  strict private
  type
    TEnumerator = class
    private
      FValue: TArray<T>;
      FIndex: NativeInt;
      function GetCurrent: T;
    public
      constructor Create(const AValue: TArray<T>);
      function MoveNext: Boolean;
      property Current: T read GetCurrent;
    end;
  public
    function GetEnumerator(): TEnumerator;
  strict private
    FData: TArray<T>;
    function GetRawData: TArray<T>;
    function GetElements(Index: Integer): T;
    procedure SetElements(Index: Integer; const Value: T);
  private
    class function EqualArray(A, B: TArray<T>): Boolean; static;
    class function CompareT(const A, B: T): Boolean; static;
    class procedure CopyArray(var FromArray, ToArray: TArray<T>;
      FromIndex: NativeInt = 0; ToIndex: NativeInt = 0;
      Count: NativeInt = -1); static;
    class procedure MoveArray(var AArray: array of T;
      FromIndex, ToIndex, Count: Integer); static;
    class function DynArrayToTArray(const Value: array of T): TArray<T>; static;
    class function Min(A, B: NativeInt): NativeInt; static;
    procedure QuickSort(const Comparer: IComparer<T>; L, R: Integer);
  public // operators
    class operator Implicit(Value: TArray<T>): TArrayEx<T>; overload;
    class operator Implicit(Value: array of T): TArrayEx<T>; overload;
    (*
      这个无解,Delphi不允许array of T作为返回值.也就是这个转换是被废了.只好用AssignTo
      class operator Implicit(Value:  TArrayEx<T>):array of T; overload;
    *)
    class operator Implicit(Value: TArrayEx<T>): TArray<T>; overload;
    class operator Explicit(Value: TArrayEx<T>): TArray<T>; overload;
    class operator Explicit(Value: array of T): TArrayEx<T>; overload;

    class operator Add(A, B: TArrayEx<T>): TArrayEx<T>; overload;
    class operator Add(A: TArrayEx<T>; const B: T): TArrayEx<T>; overload;
    class operator Add(const A: T; B: TArrayEx<T>): TArrayEx<T>; overload;
    class operator Add(A: TArrayEx<T>; B: array of T): TArrayEx<T>; overload;
    class operator Add(A: array of T; B: TArrayEx<T>): TArrayEx<T>; overload;
    class operator In (A: T; B: TArrayEx<T>): Boolean; overload;
    //
    class operator Equal(A, B: TArrayEx<T>): Boolean; overload;
    class operator Equal(A: TArrayEx<T>; B: TArray<T>): Boolean; overload;
    class operator Equal(A: TArray<T>; B: TArrayEx<T>): Boolean; overload;
    class operator Equal(A: array of T; B: TArrayEx<T>): Boolean; overload;
    class operator Equal(A: TArrayEx<T>; B: array of T): Boolean; overload;

  public
    procedure SetLen(Value: NativeInt); inline;
    function GetLen: NativeInt; inline;
    function ByteLen: NativeInt; inline;
    class function Create(Value: array of T): TArrayEx<T>; overload; static;
    class function Create(Value: TArrayEx<T>): TArrayEx<T>; overload; static;
    class function Create(const Value: T): TArrayEx<T>; overload; static;
    function Clone(): TArrayEx<T>;
    procedure SetValue(Value: array of T);
    function ToArray(): TArray<T>;
    function SubArray(AFrom, ACount: NativeInt): TArrayEx<T>;
    procedure Delete(AFrom, ACount: NativeInt); overload;
    procedure Delete(AIndex: NativeInt); overload;
    procedure Append(Values: TArrayEx<T>); overload;
    procedure Append(const Value: T); overload;
    procedure Append(Values: array of T); overload;
    procedure Append(Values: TArray<T>); overload;
    function Insert(AIndex: NativeInt; const Value: T): NativeInt; overload;
    function Insert(AIndex: NativeInt; const Values: array of T)
      : NativeInt; overload;
    function Insert(AIndex: NativeInt; const Values: TArray<T>)
      : NativeInt; overload;
    function Insert(AIndex: NativeInt; const Values: TArrayEx<T>)
      : NativeInt; overload;
    procedure Unique();
    // 排序
    procedure Sort(); overload;
    procedure Sort(const Comparer: IComparer<T>); overload;
    procedure Sort(const Comparer: IComparer<T>;
      Index, Count: Integer); overload;
    // 搜索
    function BinarySearch(const Item: T; out FoundIndex: Integer;
      const Comparer: IComparer<T>; Index, Count: Integer): Boolean; overload;
    function BinarySearch(const Item: T; out FoundIndex: Integer;
      const Comparer: IComparer<T>): Boolean; overload;
    function BinarySearch(const Item: T; out FoundIndex: Integer)
      : Boolean; overload;

    property Size: NativeInt read GetLen write SetLen;
    property Len: NativeInt read GetLen write SetLen;
    property RawData: TArray<T> read GetRawData;
    property Elements[Index: Integer]: T read GetElements
      write SetElements; default;
  end;

implementation

uses System.RTLConsts;

class operator TArrayEx<T>.Add(A, B: TArrayEx<T>): TArrayEx<T>;
begin
  Result := A.Clone;
  Result.Append(B);
end;

class operator TArrayEx<T>.Add(A: TArrayEx<T>; const B: T): TArrayEx<T>;
begin
  Result := A.Clone;
  Result.Append(B);
end;

class operator TArrayEx<T>.Add(const A: T; B: TArrayEx<T>): TArrayEx<T>;
begin
  Result.SetValue([A]);
  Result.Append(B);
end;

class operator TArrayEx<T>.Add(A: TArrayEx<T>; B: array of T): TArrayEx<T>;
begin
  Result := A.Clone;
  Result.Append(B);
end;

class operator TArrayEx<T>.Add(A: array of T; B: TArrayEx<T>): TArrayEx<T>;
begin
  Result.FData := DynArrayToTArray(A);
  Result.Append(B);
end;

class operator TArrayEx<T>.In(A: T; B: TArrayEx<T>): Boolean;
var
  Tmp: T;
begin
  Result := False;
  for Tmp in B.FData do
    if CompareT(A, Tmp) then
    begin
      Result := True;
      Break;
    end;
end;

class operator TArrayEx<T>.Equal(A, B: TArrayEx<T>): Boolean;
begin
  Result := EqualArray(A.FData, B.FData);
end;

class operator TArrayEx<T>.Equal(A: TArrayEx<T>; B: TArray<T>): Boolean;
begin
  Result := EqualArray(A.FData, B);
end;

class operator TArrayEx<T>.Equal(A: TArray<T>; B: TArrayEx<T>): Boolean;
begin
  Result := EqualArray(A, B.FData);
end;

class operator TArrayEx<T>.Equal(A: array of T; B: TArrayEx<T>): Boolean;
begin
  Result := EqualArray(DynArrayToTArray(A), B.FData);
end;

class operator TArrayEx<T>.Equal(A: TArrayEx<T>; B: array of T): Boolean;
begin
  Result := EqualArray(A.FData, DynArrayToTArray(B));
end;

function TArrayEx<T>.BinarySearch(const Item: T; out FoundIndex: Integer;
  const Comparer: IComparer<T>; Index, Count: Integer): Boolean;
var
  L, H: Integer;
  mid, cmp: Integer;
begin
  if (Index < Low(FData)) or ((Index > High(FData)) and (Count > 0)) or
    (Index + Count - 1 > High(FData)) or (Count < 0) or (Index + Count < 0) then
    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
  if Count = 0 then
  begin
    FoundIndex := Index;
    Exit(False);
  end;

  Result := False;
  L := Index;
  H := Index + Count - 1;
  while L <= H do
  begin
    mid := L + (H - L) shr 1;
    cmp := Comparer.Compare(FData[mid], Item);
    if cmp < 0 then
      L := mid + 1
    else
    begin
      H := mid - 1;
      if cmp = 0 then
        Result := True;
    end;
  end;
  FoundIndex := L;
end;

function TArrayEx<T>.BinarySearch(const Item: T; out FoundIndex: Integer;
  const Comparer: IComparer<T>): Boolean;
begin
  Result := BinarySearch(Item, FoundIndex, Comparer, Low(FData), Length(FData));
end;

function TArrayEx<T>.BinarySearch(const Item: T;
  out FoundIndex: Integer): Boolean;
begin
  Result := BinarySearch(Item, FoundIndex, TComparer<T>.Default, Low(FData),
    Length(FData));
end;

function TArrayEx<T>.ByteLen: NativeInt;
begin
  Result := Length(FData) * Sizeof(T);
end;

class function TArrayEx<T>.Min(A, B: NativeInt): NativeInt;
begin
  Result := A;
  if Result > B then
    Result := B;
end;

class procedure TArrayEx<T>.CopyArray(var FromArray, ToArray: TArray<T>;
  FromIndex, ToIndex, Count: NativeInt);
var
  i: Integer;
begin
  if Count = 0 then
    Exit;
  if Count < 0 then
    Count := Min(Length(FromArray), Length(ToArray));
  if Length(FromArray) < (FromIndex + Count) then
    Count := Length(FromArray) - FromIndex;
  if Length(ToArray) < (ToIndex + Count) then
    Count := Length(ToArray) - ToIndex;

  if Count > 0 then
    for i := 0 to Count - 1 do
      ToArray[ToIndex + i] := FromArray[FromIndex + i];
end;

class procedure TArrayEx<T>.MoveArray(var AArray: array of T;
  FromIndex, ToIndex, Count: Integer);
var
  i: Integer;
begin
  if Count > 0 then
  begin
    if FromIndex < ToIndex then
      for i := Count - 1 downto 0 do
        AArray[ToIndex + i] := AArray[FromIndex + i]
    else if FromIndex > ToIndex then
      for i := 0 to Count - 1 do
        AArray[ToIndex + i] := AArray[FromIndex + i];
  end;
end;

procedure TArrayEx<T>.QuickSort(const Comparer: IComparer<T>; L, R: Integer);
var
  i, J: Integer;
  pivot, temp: T;
begin
  if (Length(FData) = 0) or ((R - L) <= 0) then
    Exit;
  repeat
    i := L;
    J := R;
    pivot := FData[L + (R - L) shr 1];
    repeat
      while Comparer.Compare(FData[i], pivot) < 0 do
        Inc(i);
      while Comparer.Compare(FData[J], pivot) > 0 do
        Dec(J);
      if i <= J then
      begin
        if i <> J then
        begin
          temp := FData[i];
          FData[i] := FData[J];
          FData[J] := temp;
        end;
        Inc(i);
        Dec(J);
      end;
    until i > J;
    if L < J then
      QuickSort(Comparer, L, J);
    L := i;
  until i >= R;
end;

class function TArrayEx<T>.DynArrayToTArray(const Value: array of T): TArray<T>;
var
  i: Integer;
begin
  SetLength(Result, Length(Value));
  for i := Low(Value) to High(Value) do
    Result[i] := Value[i];
end;

class function TArrayEx<T>.EqualArray(A, B: TArray<T>): Boolean;
var
  i: Integer;
begin
  Result := True;
  if A = B then
    Exit;
  if Length(A) <> Length(B) then
  begin
    Result := False;
  end
  else
  begin
    for i := Low(A) to High(A) do
      if not CompareT(A[i], B[i]) then
      begin
        Result := False;
        Break;
      end;
  end;
end;

class function TArrayEx<T>.CompareT(const A, B: T): Boolean;
var
  Compare: IComparer<T>;
begin
  Compare := TComparer<T>.Default;
  Result := Compare.Compare(A, B) = 0;
end;
// class function TArrayEx<T>.CompareT(const A, B: T): Boolean;
// var
// p1, p2: PByte;
// i: Integer;
// begin
// Result := True;
// p1 := PByte(@A);
// p2 := PByte(@B);
// for i := 0 to Sizeof(T) - 1 do
// begin
// //
// if p1^ <> p2^ then
// begin
// Result := False;
// Exit;
// end;
// Inc(p1);
// Inc(p2);
// end;
// end;

function TArrayEx<T>.GetElements(Index: Integer): T;
begin
  Result := FData[Index];
end;

function TArrayEx<T>.GetEnumerator: TEnumerator;
begin
  Result := TEnumerator.Create(FData);
end;

function TArrayEx<T>.GetLen: NativeInt;
begin
  Result := Length(FData);
end;

function TArrayEx<T>.GetRawData: TArray<T>;
begin
  Result := FData;
end;

class operator TArrayEx<T>.Implicit(Value: TArrayEx<T>): TArray<T>;
begin
  SetLength(Result, Length(Value.FData));
  CopyArray(Value.FData, Result, 0, 0, Length(Value.FData));
end;

class operator TArrayEx<T>.Explicit(Value: array of T): TArrayEx<T>;
begin
  Result.SetValue(Value);
end;

class operator TArrayEx<T>.Implicit(Value: array of T): TArrayEx<T>;
begin
  Result.SetValue(Value);
end;

class operator TArrayEx<T>.Implicit(Value: TArray<T>): TArrayEx<T>;
begin
  SetLength(Result.FData, Length(Value));
  CopyArray(Value, Result.FData, 0, 0, Length(Value));
end;

class operator TArrayEx<T>.Explicit(Value: TArrayEx<T>): TArray<T>;
begin
  SetLength(Result, Length(Value.FData));
  CopyArray(Value.FData, Result, 0, 0, Length(Value.FData));
end;

procedure TArrayEx<T>.SetElements(Index: Integer; const Value: T);
begin
  FData[Index] := Value;
end;

procedure TArrayEx<T>.SetLen(Value: NativeInt);
begin
  SetLength(FData, Value);
end;

procedure TArrayEx<T>.SetValue(Value: array of T);
begin
  FData := DynArrayToTArray(Value);
end;

procedure TArrayEx<T>.Sort;
begin
  QuickSort(TComparer<T>.Default, Low(FData), High(FData));
end;

procedure TArrayEx<T>.Sort(const Comparer: IComparer<T>; Index, Count: Integer);
begin
  if (Index < Low(FData)) or ((Index > High(FData)) and (Count > 0)) or
    (Index + Count - 1 > High(FData)) or (Count < 0) or (Index + Count < 0) then
    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
  if Count <= 1 then
    Exit;
  QuickSort(Comparer, Index, Index + Count - 1);
end;

procedure TArrayEx<T>.Sort(const Comparer: IComparer<T>);
begin
  QuickSort(Comparer, Low(FData), High(FData));
end;

function TArrayEx<T>.ToArray(): TArray<T>;
begin
  SetLength(Result, Length(FData));
  CopyArray(FData, Result, 0, 0, Length(FData));
end;

class function TArrayEx<T>.Create(Value: array of T): TArrayEx<T>;
begin
  Result.SetValue(Value);
end;

class function TArrayEx<T>.Create(Value: TArrayEx<T>): TArrayEx<T>;
begin
  Result := Value.Clone;
end;

class function TArrayEx<T>.Create(const Value: T): TArrayEx<T>;
begin
  Result.SetValue([Value]);
end;

function TArrayEx<T>.Clone(): TArrayEx<T>;
begin
  Result := SubArray(0, Length(FData));
end;

function TArrayEx<T>.SubArray(AFrom, ACount: NativeInt): TArrayEx<T>;
begin
  SetLength(Result.FData, ACount);
  CopyArray(FData, Result.FData, AFrom, 0, ACount);
end;

procedure TArrayEx<T>.Delete(AFrom, ACount: NativeInt);
begin
  if AFrom >= Length(FData) then
    Exit;
  if (AFrom + ACount) > Length(FData) then
    ACount := Length(FData) - AFrom;

  MoveArray(FData, AFrom + ACount, AFrom, Length(FData) - (AFrom + ACount));
  SetLength(FData, Length(FData) - ACount);
end;

procedure TArrayEx<T>.Delete(AIndex: NativeInt);
begin
  Delete(AIndex, 1);
end;

procedure TArrayEx<T>.Append(Values: TArrayEx<T>);
begin
  Insert(Length(FData), Values);
end;

procedure TArrayEx<T>.Append(Values: TArray<T>);
begin
  Insert(Length(FData), Values);
end;

procedure TArrayEx<T>.Append(const Value: T);
begin
  SetLength(FData, Length(FData) + 1);
  FData[High(FData)] := Value;
end;

procedure TArrayEx<T>.Append(Values: array of T);
begin
  Insert(Length(FData), Values);
end;

function TArrayEx<T>.Insert(AIndex: NativeInt; const Value: T): NativeInt;
var
  i: Integer;
begin
  Result := -1;
  if (AIndex > Length(FData)) or (AIndex < 0) then
    Exit;
  SetLength(FData, Length(FData) + 1);
  MoveArray(FData, AIndex, AIndex + 1, Length(FData) - AIndex);
  FData[AIndex] := Value;
  Result := AIndex;
end;

function TArrayEx<T>.Insert(AIndex: NativeInt; const Values: array of T)
  : NativeInt;
var
  i: Integer;
begin
  SetLength(FData, Length(FData) +Length(Values));
  MoveArray(FData, AIndex, AIndex + Length(Values), Length(FData) - AIndex);
  for i := 0 to Length(Values) - 1 do
    FData[AIndex + i] := Values[i];
  Result := AIndex;
end;

function TArrayEx<T>.Insert(AIndex: NativeInt; const Values: TArray<T>)
  : NativeInt;
var
  i: Integer;
begin
  SetLength(FData, Length(FData) + Length(Values));
  MoveArray(FData, AIndex, AIndex + Length(Values), Length(FData) - AIndex);
  for i := 0 to Length(Values) - 1 do
    FData[AIndex + i] := Values[i];
  Result := AIndex;
end;

function TArrayEx<T>.Insert(AIndex: NativeInt; const Values: TArrayEx<T>)
  : NativeInt;
begin
  Result := Insert(AIndex, Values.ToArray);
end;

procedure TArrayEx<T>.Unique();
var
  i, J: Integer;
  Tmp: TArrayEx<T>;
  Flag: Boolean;
begin

  for i := High(FData) downto Low(FData) do
  begin
    Flag := False;
    for J := High(Tmp.FData) downto Low(Tmp.FData) do
    begin
      if CompareT(FData[i], Tmp[J]) then
      begin
        Flag := True;
        Break;
      end;
    end;
    if not Flag then
      Tmp.Append(FData[i]);
  end;
  FData := Tmp.FData;
end;

{ TArrayEx<T>.TEnumerator }

constructor TArrayEx<T>.TEnumerator.Create(const AValue: TArray<T>);
begin
  FValue := AValue;
  FIndex := -1;
end;

function TArrayEx<T>.TEnumerator.GetCurrent: T;
begin
  Result := FValue[FIndex];
end;

function TArrayEx<T>.TEnumerator.MoveNext: Boolean;
begin
  Result := False;
  if (FIndex >= Length(FValue)) then
    Exit;

  Inc(FIndex);
  Result := FIndex < Length(FValue);
end;

end.

  

 

posted @ 2019-03-01 23:33  _成飞  阅读(1276)  评论(0编辑  收藏  举报