zoukankan      html  css  js  c++  java
  • 看看Delphi中的列表(List)和泛型

    前言

            最开始学习数据结构的时候,链表,堆栈,队列,数组,似乎只是一堆概念,随着使用中慢慢接触,其对应的模型,功能,一个个跃到眼前,变成了复杂模型数据处理中的最重要的部分。---By Murphy 20180424

    1,列表

    Delphi中的列表有很多,从数据结构上可以分作:TList(数据链),TQueue(队列),TStack(堆栈),TDictionary(字典表)等等; 而从功能上又可以分作TList(列表),TObjectList(对象列表),TComponentList(组件列表),TStringList(字串列表),TClassList(类列表)。

    列表的数据结构性,决定了列表的基础方法,基本都是以增删改查,计数,排序为主;而功能列表中,进一步增强了对每个节点元素的检查和限制,从而增加了针对这种数据类型的更贴合的功能,例如:对象的自动销毁,或仅结构移除,构建排序比较规则等等。现在就让我们看几种具有代表性的列表

    a,TList

    我们首先来看看TList在DelphiRX10中的源码定义

      TList = class(TObject)
      private
        FList: TPointerList;     
        FCount: Integer;
        FCapacity: Integer;
      protected
        function Get(Index: Integer): Pointer;
        procedure Grow; virtual;
        procedure Put(Index: Integer; Item: Pointer);
        procedure Notify(Ptr: Pointer; Action: TListNotification); virtual;
        procedure SetCapacity(NewCapacity: Integer);
        procedure SetCount(NewCount: Integer);
      public
        type
          TDirection = System.Types.TDirection;

        destructor Destroy; override;
        function Add(Item: Pointer): Integer;                                                                               //添加一个新元素
        procedure Clear; virtual;                                                                                                  //清空列表
        procedure Delete(Index: Integer);                                                                                    //删除对象元素并销毁
        class procedure Error(const Msg: string; Data: NativeInt); overload; virtual;
        class procedure Error(Msg: PResStringRec; Data: NativeInt); overload;
        procedure Exchange(Index1, Index2: Integer);                                                                //交换对象
        function Expand: TList;                                                                                                     //扩展当前对象容量
        function Extract(Item: Pointer): Pointer; inline;                                                                  //移除对象元素,而不是销毁,并且返回对象指针
        function ExtractItem(Item: Pointer; Direction: TDirection): Pointer;                                   //从当前对象指针开始定向定位,查找到对应对象并移除  
        function First: Pointer; inline;                                                                                             //当前对象指针移动到第一个元素
        function GetEnumerator: TListEnumerator;                                                                       //一个抽象列表,没看懂这里的扩展性设计
        function IndexOf(Item: Pointer): Integer;                                                                           //检索元素
        function IndexOfItem(Item: Pointer; Direction: TDirection): Integer;
        procedure Insert(Index: Integer; Item: Pointer);                                                                //插入一个对象元素
        function Last: Pointer;                                                                                                       //当前对象指针移动到最后一个元素上
        procedure Move(CurIndex, NewIndex: Integer);                                                              //将当前对象移动位置
        function Remove(Item: Pointer): Integer; inline;                                                               //移除对象
        function RemoveItem(Item: Pointer; Direction: TDirection): Integer;
        procedure Pack;                                                                                                               //清空空对象

       //TListSortCompare = function (Item1, Item2: Pointer): Integer;
       //TListSortCompareFunc = reference to function (Item1, Item2: Pointer): Integer;
        procedure Sort(Compare: TListSortCompare);                                                                //排序函数,这里TListSortCompare对应的是排序函数的指针
        procedure SortList(const Compare: TListSortCompareFunc);                                         //TListSortCompareFunc是对应TListSortCompare的匿名函数,方法定义更为灵活
        procedure Assign(ListA: TList; AOperator: TListAssignOp = laCopy; ListB: TList = nil);  //指定内容来源
        property Capacity: Integer read FCapacity write SetCapacity;                                         //空间计数
        property Count: Integer read FCount write SetCount;                                                      //元素计数
        property Items[Index: Integer]: Pointer read Get write Put; default;
        property List: TPointerList read FList;                                                                              //指针列表
      end

    我们可以看到,这个TList算所有列表之源,具备了所有列表类型的基础方法属性。

    b, TObjectList

    这种List的单元Contnrs需要额外引用一下,我们在这个类的源码中可以看到,几乎所有的方法名跟TList都是一致的,只不过元素对象参数由TPoint变成了TObject,另外,还增加了以下两个新的方法:

        constructor Create(AOwnsObjects: Boolean); overload;       //重载了一个构造方法,这里可以在创建的时候直接指定其拥有者,如果是False,就跟TList差别很大了,相当于这个ObjectList是独立于元素存在的。

                                                                                                      //这时候元素的空间需要额外的管理

        property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;   //这个是ObjectList的一个属性,可以由构造方法带进来,也可以使用时改变。

    c,TComponetList

    这个是直接继承TObjectList的,同样的,所有元素由基础对象改成了组件。但是这个类中也增加了一个方法:

         procedure HandleFreeNotify(Sender: TObject; AComponent: TComponent);            //这个方法对应着procedure TComponent.FreeNotification(AComponent: TComponent);方法

    这个方法使得其管理的对象释放更为灵活,释放一个元素组件的时候,不需要操作ComponentList ,可以直接操作元素,并通过元素组件的方法自动触发TComponentList来处理链结构。

    d,TStringList

    TStringList跟TList并不是一脉相承的,而是继承TStrings的,而TStrings是继承TPersistent的,其方法完全是为字串服务的,跟TList有相似点,其变化又比TList系列更为复杂。因为其具备

    一系列增加和管理对象的方法:

    property Objects[Index: Integer]: TObject read GetObject write PutObject;

    function AddObject(const S: string; AObject: TObject): Integer; virtual;

    function IndexOfObject(AObject: TObject): Integer; virtual;

    procedure InsertObject(Index: Integer; const S: string; AObject: TObject); virtual;

    function ToObjectArray: TArray<TObject>;

    除此之外,还具备字典配对的功能:

         property Names[Index: Integer]: string read GetName;

        property KeyNames[Index: Integer]: string read GetKeyName;

        property Values[const Name: string]: string read GetValue write SetValue;
        property ValueFromIndex[Index: Integer]: string read GetValueFromIndex write SetValueFromIndex;

    这使得其功能和扩展性都显得极为强大,TStrings是个抽象类,而TStringList增加了Sort方法和对象耦合属性OwnsObjects。 当没有使用对象和字典配对的时候,TStringList看起来就象一个简单的字串数组。

    e,TClassList

    这个类似乎不需要额外谈的,类中类的列表形态,批量处理的情景除非是特别复杂的模型才可以用到。其继承类是TList,没有对象管理的拥有者功能。

    2,泛型

    泛型的出现,直接弄得各种List似乎都变成了一种兼容的特例模式了。泛型的可以选择任何类型的数据元素,并可以以任何数据结构模式进行组合。虽然在数据定义阶段略显复杂,但是作为一种强力的数据扩展

    可以应用的场景也多得多。

    泛型的引用单元:Generics.Collections,泛型的定义模式:

    a,泛型数据类型的构成基础

    我们先来看看泛型单元中的前四个数据定义,他们是泛型构成的基础件:TArray(TObject),TEnumerator<T>(TObject),TEnumerable<T>(TObject),TListHelper(TObject)

      TArray = class
      private
        class procedure QuickSort<T>(var Values: array of T; const Comparer: IComparer<T>;
          L, R: Integer); static;
        class procedure CheckArrays(Source, Destination: Pointer; SourceIndex, SourceLength, DestIndex, DestLength, Count: NativeInt); static;
      public
        class procedure Sort<T>(var Values: array of T); overload; static;
        class procedure Sort<T>(var Values: array of T; const Comparer: IComparer<T>); overload; static;
        class procedure Sort<T>(var Values: array of T;
          const Comparer: IComparer<T>; Index, Count: Integer); overload; static;

        class function BinarySearch<T>(const Values: array of T; const Item: T;
          out FoundIndex: Integer; const Comparer: IComparer<T>;
          Index, Count: Integer): Boolean; overload; static;
        class function BinarySearch<T>(const Values: array of T; const Item: T;
          out FoundIndex: Integer; const Comparer: IComparer<T>): Boolean; overload; static;
        class function BinarySearch<T>(const Values: array of T; const Item: T;
          out FoundIndex: Integer): Boolean; overload; static; static;

        class procedure Copy<T>(const Source: array of T; var Destination: array of T; SourceIndex, DestIndex, Count: NativeInt); overload; static;
        class procedure Copy<T>(const Source: array of T; var Destination: array of T; Count: NativeInt); overload; static;
      end;

    这是额外为TArray定义的一段代码,本人看得不是特别懂,感觉这个主要的功能是为System中的TArray<T>定义作一个扩展。

    注意,TArray<T>的定义是出现在System单元中:TArray<T> = array of T;

    这个TArray扩展为后续的泛型定义,提供了一个本体。接下来,我们看看两个抽象类:TEnumerator<T>,TEnumerable<T>.

      TEnumerator<T> = class abstract
      protected
        function DoGetCurrent: T; virtual; abstract;
        function DoMoveNext: Boolean; virtual; abstract;
      public
        property Current: T read DoGetCurrent;
        function MoveNext: Boolean;
      end;

      TEnumerable<T> = class abstract
      private
      {$HINTS OFF}
        function ToArrayImpl(Count: Integer): TArray<T>; // used by descendants
      {$HINTS ON}
      protected
        function DoGetEnumerator: TEnumerator<T>; virtual; abstract;
      public
        destructor Destroy; override;
        function GetEnumerator: TEnumerator<T>;
        function ToArray: TArray<T>; virtual;
      end;

    这两个抽象类,TEnumerable是所有泛型的基类,定义出泛型的数据实体是TArray<T>模式,并且引用了另外一个抽象方法类TEnumerator ,使得泛型具备获取当前元素Current以及MoveNext导航功能。

    这个设计很重要,我们可以看到Current和MoveNext在这里不再是数据结构的属性,而是作为元素基础功能出现的。

    最后,我们再来看看一个附加的结构体:TListHelper = record ,它十分复杂,我没有太多时间去详细研究,暂时看到它是作为泛型的扩展属性使用的,所以就认为它是一个泛型的帮助信息扩展吧。

    b,TList<T>

    这个类型的篇幅还是比较长的,比TList要复杂多了,既融合了TList的基础定义,又加入了泛型的实现基础,下面我们一起看一看其定义的过程。

      TList<T> = class(TEnumerable<T>)
      private type
        arrayofT = array of T;                                                                                           //定义了一个内部元数组类
      var
        FListHelper: TListHelper; // FListHelper must always be followed by FItems     //暂且认为是帮助信息类,不做过多分析
        FItems: arrayofT; // FItems must always be preceded by FListHelper                //利用元数组类实现了一个元数据实体
        FComparer: IComparer<T>;                                                                               //为排序而诞生的比较方法指针
        FOnNotify: TCollectionNotifyEvent<T>;                                                             //内部事件

        function GetCapacity: Integer; inline;                                                                 //得到整个泛型的内存空间
        procedure SetCapacity(Value: Integer); overload; inline;                                  //动态设置元素的Length
        procedure SetCount(Value: Integer); inline;                                                      //动态设置元素个数
        function GetItem(Index: Integer): T; inline;                                                        //根据序号获得指定元素
        procedure SetItem(Index: Integer; const Value: T); inline;
        procedure GrowCheck(ACount: Integer); inline;                                              //Helper相关
        procedure DoDelete(Index: Integer; Notification: TCollectionNotification); inline;      //根据序号删除并销毁某元素
        procedure InternalNotify(const Item; Action: TCollectionNotification);                      
        function InternalCompare(const Left, Right): Integer;
        property FCount: Integer read FListHelper.FCount write FListHelper.FCount;
      protected
        function ItemValue(const Item: T): NativeInt;                                                 //返回元素的值,NativeInt是内存中一种Int编码值。
        function DoGetEnumerator: TEnumerator<T>; override;                               //重新构建元素的导航功能。
        procedure Notify(const Item: T; Action: TCollectionNotification); virtual;
      public
      type
        TDirection = System.Types.TDirection;                                                        //定义方向,用于List的定向操作
        TEmptyFunc = reference to function (const L, R: T): Boolean;                    //为空时的匿名函数
        TListCompareFunc = reference to function (const L, R: T): Integer;            //为排序比较时的匿名函数

       //再往下看基本就没什么新方法了,功能都是结合TList和TEnumerable的功能重组.

       constructor Create; overload;
        constructor Create(const AComparer: IComparer<T>); overload;
        constructor Create(const Collection: TEnumerable<T>); overload;
        destructor Destroy; override;

        class procedure Error(const Msg: string; Data: NativeInt); overload; virtual;
    {$IFNDEF NEXTGEN}
        class procedure Error(Msg: PResStringRec; Data: NativeInt); overload;
    {$ENDIF  NEXTGEN}

        function Add(const Value: T): Integer; inline;

        procedure AddRange(const Values: array of T); overload;
        procedure AddRange(const Collection: IEnumerable<T>); overload; inline;
        procedure AddRange(const Collection: TEnumerable<T>); overload; inline;

        procedure Insert(Index: Integer; const Value: T); inline;

        procedure InsertRange(Index: Integer; const Values: array of T); overload;
        procedure InsertRange(Index: Integer; const Collection: IEnumerable<T>); overload;
        procedure InsertRange(Index: Integer; const Collection: TEnumerable<T>); overload;

        procedure Pack; overload;
        procedure Pack(const IsEmpty: TEmptyFunc); overload;

        function Remove(const Value: T): Integer; inline;
        function RemoveItem(const Value: T; Direction: TDirection): Integer; inline;
        procedure Delete(Index: Integer); inline;
        procedure DeleteRange(AIndex, ACount: Integer); inline;
        function ExtractItem(const Value: T; Direction: TDirection): T; inline;
        function Extract(const Value: T): T; inline;

        procedure Exchange(Index1, Index2: Integer); inline;
        procedure Move(CurIndex, NewIndex: Integer); inline;

        function First: T; inline;
        function Last: T; inline;

        procedure Clear; inline;

        function Expand: TList<T>; inline;

        function Contains(const Value: T): Boolean; inline;
        function IndexOf(const Value: T): Integer; inline;
        function IndexOfItem(const Value: T; Direction: TDirection): Integer; inline;
        function LastIndexOf(const Value: T): Integer; inline;

        procedure Reverse; inline;

        procedure Sort; overload;
        procedure Sort(const AComparer: IComparer<T>); overload;
        function BinarySearch(const Item: T; out Index: Integer): Boolean; overload;
        function BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>): Boolean; overload;

        procedure TrimExcess; inline;

        function ToArray: TArray<T>; override; final;

        property Capacity: Integer read GetCapacity write SetCapacity;
        property Count: Integer read FListHelper.FCount write SetCount;
        property Items[Index: Integer]: T read GetItem write SetItem; default;
        property List: arrayofT read FItems;

        property OnNotify: TCollectionNotifyEvent<T> read FOnNotify write FOnNotify;

        type                                                                                                         //这里重新指定了元素导航的功能实现
          TEnumerator = class(TEnumerator<T>)
          private
            FList: TList<T>;
            FIndex: Integer;
            function GetCurrent: T;
          protected
            function DoGetCurrent: T; override;
            function DoMoveNext: Boolean; override;
          public
            constructor Create(const AList: TList<T>);
            property Current: T read GetCurrent;
            function MoveNext: Boolean;
          end;

        function GetEnumerator: TEnumerator; reintroduce; inline;
      end;

    该类应该是泛型中使用最为广泛的一个类,我们既可以用以后的类来组合使用,又可以重新定义其各项基础功能实现扩展。

    直接使用的模式: DataSetList: TList<TDataSet>;

    重新定义的模式:

    type

          TSpecList<T>=class(TList<T>)

         ....

    end;

    使用的时候直接将<T>实化:DataSetList: TSpecList<TDataSet>;

    c,其他几种使用频率比较高的泛型:

    TQueue<T> = class(TEnumerable<T>)                           //队列泛型,定义几乎跟TList<T> 一样,所以这里的方法说明省去。

    TThreadList<T> = class                                                   //线程泛型,实体是TList<T>,针对其控制增加了锁定方法。

    TStack<T> = class(TEnumerable<T>)                            //堆栈泛型,增加了Pop和Push方法,其他定义几乎跟TList<T>一致。

      TPair<TKey,TValue> = record
        Key: TKey;
        Value: TValue;
        constructor Create(const AKey: TKey; const AValue: TValue);
      end;
      TDictionary<TKey,TValue> = class(TEnumerable<TPair<TKey,TValue>>)      //字典泛型,这个比较有用,数据是以Key和Value成对出现。主要是增加了Hash方法,各种元素操作也都以Key作为参数。

    这些泛型都是基于数据结构的变化。

    d,与对象相关的泛型扩展 TObjectList<T>,

    TObjectList<T: class> = class(TList<T>)   

      private
        FOwnsObjects: Boolean;
      protected
        procedure Notify(const Value: T; Action: TCollectionNotification); override;
      public
        constructor Create(AOwnsObjects: Boolean = True); overload;
        constructor Create(const AComparer: IComparer<T>; AOwnsObjects: Boolean = True); overload;
        constructor Create(const Collection: TEnumerable<T>; AOwnsObjects: Boolean = True); overload;
        destructor Destroy; override;
        property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;                                         //主要就是增加了这个属性,让其元素耦合性降低
      end;

    TObjectQueue<T: class> = class(TQueue<T>)

    TObjectStack<T: class> = class(TStack<T>)

    TObjectDictionary<TKey,TValue> = class(TDictionary<TKey,TValue>)

    以上这四种泛型都是在原有泛型上的轻微扩展,同列表的对象扩展一样。最主要的属性就是OwnsObjects,使得整个泛型数据集,可以拥有对象空间的独立管理能力。

    3,小结

    为什么我们要使用泛型?泛型数据的优势和劣势又各是什么呢?

    其实Delphi引用泛型数据算比较落后的了,Java很早就有泛型概念,而FrameWork也是从2.0 开始就引入了泛型数据,而Delphi是从Delphi2009~Delphi2010才正式引入泛型。

    泛型的早期模式,其实就是各种列表,也就是本篇的第1点所阐述的内容,对比泛型和列表,其实泛型能实现的方式列表都可以实现,而列表的异构元素结构,泛型是不适合的。

    那么泛型相对列表有什么优势呢?优势主要体现在两个方面:重用性,安全高效性。

    a,重用性

    这里举个例子说明,我们如果需要两个列表TStringxxxList,TIntxxxList,如果用列表继承的概念,那就必须要写两个定义:

    type TStringList=class(TList)

    ...这可能会有count方法

    end;

    type TIntxxxList=class(TList)

    ...这可能会有count方法

    end;

     如果换作泛型,就只需要定义一次就好了,列表中通性的方法是固定的。

    type TxxxList<T>=class(TList<T>)

    ...这里可能有count方法

    end;

    使用时,我们用TxxxList<string>,TxxxList<Int>就可以取代两种List。

    b,安全高效性

    虽然用列表也可以实现子类型的存取,但是在使用过程中就免不了要进行类型转换和判断(装箱和拆箱),即不安全,也会影响到系统效率。

    例如:

      AllDataSets : TComponentList;                                 //用List进行一个数据集的存储。

      AllDataSets := TComponentList.Create(False);        //非耦合性列表
      AllDataSets.Add(ADataSet);                                   //存放一个数据集

    到这一步似乎跟泛型都没什么差异,然而取的时候就比较麻烦了。

    if AllDataSets.Items[ADataSetNo] is TDataSet then

     funxxx(AllDataSets.Items[ADataSetNo] as TDataSet);

    这不仅仅意味着要承受各种非规则数据的干扰,还必须进行强制类型转换,才能完成其初期设计的【数据集列表】这样的概念。反观泛型就要简单得多:

    定义:AllDataSets : TObjectList<TDataSet>;

    AllDataSets := TObjectList<TDataSet>.Create(False);

    AllDataSets.Add(ADataSet); 

    我们在使用中,完全不担心类型问题,直接调用就好了,而且即便真的有类型匹配的错误,在编译期就可以将其呈现出来。

  • 相关阅读:
    Java实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯VIP基础练习 矩形面积交
    Java实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    核心思想:想清楚自己创业的目的(如果你没有自信提供一种更好的产品或服务,那就别做了,比如IM 电商 搜索)
    在Linux中如何利用backtrace信息解决问题
  • 原文地址:https://www.cnblogs.com/Murphieston/p/8930881.html
Copyright © 2011-2022 走看看