此文章转载于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; f: TArrayEx ; s : TArrayEx ; // i: Integer; z: array of Integer; args:TArrayEx ; 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; f: TArrayEx ; s : TArrayEx ; // i: Integer; z: array of Integer; args:TArrayEx ; 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 = record strict private type TEnumerator = class private FValue: TArray ; FIndex: NativeInt; function GetCurrent: T; public constructor Create(const AValue: TArray ); function MoveNext: Boolean; property Current: T read GetCurrent; end; public function GetEnumerator(): TEnumerator; strict private FData: TArray ; function GetRawData: TArray ; function GetElements(Index: Integer): T; procedure SetElements(Index: Integer; const Value: T); private class function EqualArray(A, B: TArray ): Boolean; static; class function CompareT(const A, B: T): Boolean; static; class procedure CopyArray(var FromArray, ToArray: TArray ; 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 ; static; class function Min(A, B: NativeInt): NativeInt; static; procedure QuickSort(const Comparer: IComparer ; L, R: Integer); public // operators class operator Implicit(Value: TArray ): TArrayEx ; overload; class operator Implicit(Value: array of T): TArrayEx ; overload; (* 这个无解,Delphi不允许array of T作为返回值.也就是这个转换是被废了.只好用AssignTo class operator Implicit(Value: TArrayEx ):array of T; overload; *) class operator Implicit(Value: TArrayEx ): TArray ; overload; class operator Explicit(Value: TArrayEx ): TArray ; overload; class operator Explicit(Value: array of T): TArrayEx ; overload; class operator Add(A, B: TArrayEx ): TArrayEx ; overload; class operator Add(A: TArrayEx ; const B: T): TArrayEx ; overload; class operator Add(const A: T; B: TArrayEx ): TArrayEx ; overload; class operator Add(A: TArrayEx ; B: array of T): TArrayEx ; overload; class operator Add(A: array of T; B: TArrayEx ): TArrayEx ; overload; class operator In (A: T; B: TArrayEx ): Boolean; overload; // class operator Equal(A, B: TArrayEx ): Boolean; overload; class operator Equal(A: TArrayEx ; B: TArray ): Boolean; overload; class operator Equal(A: TArray ; B: TArrayEx ): Boolean; overload; class operator Equal(A: array of T; B: TArrayEx ): Boolean; overload; class operator Equal(A: TArrayEx ; 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 ; overload; static; class function Create(Value: TArrayEx ): TArrayEx ; overload; static; class function Create(const Value: T): TArrayEx ; overload; static; function Clone(): TArrayEx ; procedure SetValue(Value: array of T); function ToArray(): TArray ; function SubArray(AFrom, ACount: NativeInt): TArrayEx ; procedure Delete(AFrom, ACount: NativeInt); overload; procedure Delete(AIndex: NativeInt); overload; procedure Append(Values: TArrayEx ); overload; procedure Append(const Value: T); overload; procedure Append(Values: array of T); overload; procedure Append(Values: TArray ); 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 ) : NativeInt; overload; function Insert(AIndex: NativeInt; const Values: TArrayEx ) : NativeInt; overload; procedure Unique(); // 排序 procedure Sort(); overload; procedure Sort(const Comparer: IComparer ); overload; procedure Sort(const Comparer: IComparer ; Index, Count: Integer); overload; // 搜索 function BinarySearch(const Item: T; out FoundIndex: Integer; const Comparer: IComparer ; Index, Count: Integer): Boolean; overload; function BinarySearch(const Item: T; out FoundIndex: Integer; const Comparer: IComparer ): 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 read GetRawData; property Elements[Index: Integer]: T read GetElements write SetElements; default; end; implementation uses System.RTLConsts; class operator TArrayEx .Add(A, B: TArrayEx ): TArrayEx ; begin Result := A.Clone; Result.Append(B); end; class operator TArrayEx .Add(A: TArrayEx ; const B: T): TArrayEx ; begin Result := A.Clone; Result.Append(B); end; class operator TArrayEx .Add(const A: T; B: TArrayEx ): TArrayEx ; begin Result.SetValue([A]); Result.Append(B); end; class operator TArrayEx .Add(A: TArrayEx ; B: array of T): TArrayEx ; begin Result := A.Clone; Result.Append(B); end; class operator TArrayEx .Add(A: array of T; B: TArrayEx ): TArrayEx ; begin Result.FData := DynArrayToTArray(A); Result.Append(B); end; class operator TArrayEx .In(A: T; B: TArrayEx ): 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 .Equal(A, B: TArrayEx ): Boolean; begin Result := EqualArray(A.FData, B.FData); end; class operator TArrayEx .Equal(A: TArrayEx ; B: TArray ): Boolean; begin Result := EqualArray(A.FData, B); end; class operator TArrayEx .Equal(A: TArray ; B: TArrayEx ): Boolean; begin Result := EqualArray(A, B.FData); end; class operator TArrayEx .Equal(A: array of T; B: TArrayEx ): Boolean; begin Result := EqualArray(DynArrayToTArray(A), B.FData); end; class operator TArrayEx .Equal(A: TArrayEx ; B: array of T): Boolean; begin Result := EqualArray(A.FData, DynArrayToTArray(B)); end; function TArrayEx .BinarySearch(const Item: T; out FoundIndex: Integer; const Comparer: IComparer ; 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 .BinarySearch(const Item: T; out FoundIndex: Integer; const Comparer: IComparer ): Boolean; begin Result := BinarySearch(Item, FoundIndex, Comparer, Low(FData), Length(FData)); end; function TArrayEx .BinarySearch(const Item: T; out FoundIndex: Integer): Boolean; begin Result := BinarySearch(Item, FoundIndex, TComparer .Default, Low(FData), Length(FData)); end; function TArrayEx .ByteLen: NativeInt; begin Result := Length(FData) * Sizeof(T); end; class function TArrayEx .Min(A, B: NativeInt): NativeInt; begin Result := A; if Result > B then Result := B; end; class procedure TArrayEx .CopyArray(var FromArray, ToArray: TArray ; 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 .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 .QuickSort(const Comparer: IComparer ; 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 .DynArrayToTArray(const Value: array of T): TArray ; var i: Integer; begin SetLength(Result, Length(Value)); for i := Low(Value) to High(Value) do Result[i] := Value[i]; end; class function TArrayEx .EqualArray(A, B: TArray ): 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 .CompareT(const A, B: T): Boolean; var Compare: IComparer ; begin Compare := TComparer .Default; Result := Compare.Compare(A, B) = 0; end; // class function TArrayEx .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 .GetElements(Index: Integer): T; begin Result := FData[Index]; end; function TArrayEx .GetEnumerator: TEnumerator; begin Result := TEnumerator.Create(FData); end; function TArrayEx .GetLen: NativeInt; begin Result := Length(FData); end; function TArrayEx .GetRawData: TArray ; begin Result := FData; end; class operator TArrayEx .Implicit(Value: TArrayEx ): TArray ; begin SetLength(Result, Length(Value.FData)); CopyArray(Value.FData, Result, 0, 0, Length(Value.FData)); end; class operator TArrayEx .Explicit(Value: array of T): TArrayEx ; begin Result.SetValue(Value); end; class operator TArrayEx .Implicit(Value: array of T): TArrayEx ; begin Result.SetValue(Value); end; class operator TArrayEx .Implicit(Value: TArray ): TArrayEx ; begin SetLength(Result.FData, Length(Value)); CopyArray(Value, Result.FData, 0, 0, Length(Value)); end; class operator TArrayEx .Explicit(Value: TArrayEx ): TArray ; begin SetLength(Result, Length(Value.FData)); CopyArray(Value.FData, Result, 0, 0, Length(Value.FData)); end; procedure TArrayEx .SetElements(Index: Integer; const Value: T); begin FData[Index] := Value; end; procedure TArrayEx .SetLen(Value: NativeInt); begin SetLength(FData, Value); end; procedure TArrayEx .SetValue(Value: array of T); begin FData := DynArrayToTArray(Value); end; procedure TArrayEx .Sort; begin QuickSort(TComparer .Default, Low(FData), High(FData)); end; procedure TArrayEx .Sort(const Comparer: IComparer ; 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 .Sort(const Comparer: IComparer ); begin QuickSort(Comparer, Low(FData), High(FData)); end; function TArrayEx .ToArray(): TArray ; begin SetLength(Result, Length(FData)); CopyArray(FData, Result, 0, 0, Length(FData)); end; class function TArrayEx .Create(Value: array of T): TArrayEx ; begin Result.SetValue(Value); end; class function TArrayEx .Create(Value: TArrayEx ): TArrayEx ; begin Result := Value.Clone; end; class function TArrayEx .Create(const Value: T): TArrayEx ; begin Result.SetValue([Value]); end; function TArrayEx .Clone(): TArrayEx ; begin Result := SubArray(0, Length(FData)); end; function TArrayEx .SubArray(AFrom, ACount: NativeInt): TArrayEx ; begin SetLength(Result.FData, ACount); CopyArray(FData, Result.FData, AFrom, 0, ACount); end; procedure TArrayEx .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 .Delete(AIndex: NativeInt); begin Delete(AIndex, 1); end; procedure TArrayEx .Append(Values: TArrayEx ); begin Insert(Length(FData), Values); end; procedure TArrayEx .Append(Values: TArray ); begin Insert(Length(FData), Values); end; procedure TArrayEx .Append(const Value: T); begin SetLength(FData, Length(FData) + 1); FData[High(FData)] := Value; end; procedure TArrayEx .Append(Values: array of T); begin Insert(Length(FData), Values); end; function TArrayEx .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 .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 .Insert(AIndex: NativeInt; const Values: TArray ) : 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 .Insert(AIndex: NativeInt; const Values: TArrayEx ) : NativeInt; begin Result := Insert(AIndex, Values.ToArray); end; procedure TArrayEx .Unique(); var i, J: Integer; Tmp: TArrayEx ; 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 .TEnumerator } constructor TArrayEx .TEnumerator.Create(const AValue: TArray ); begin FValue := AValue; FIndex := -1; end; function TArrayEx .TEnumerator.GetCurrent: T; begin Result := FValue[FIndex]; end; function TArrayEx .TEnumerator.MoveNext: Boolean; begin Result := False; if (FIndex >= Length(FValue)) then Exit; Inc(FIndex); Result := FIndex < Length(FValue); end; end.