Delphi中的容器类

TList 类实际上就是一个可以存储指针的容器类,提供了一系列的方法和属性来添加,删除,重排,定位,存取和排序容器中的类,它是基于数组的机制来实现的容器,比较类似于C++中的Vector和Java中的ArrayList,TList 经常用来保存一组对象列表,基于数组实现的机制使得用下标存取容器中的对象非常快,但是随着容器中的对象的增多,插入和删除对象速度会直线下降,因此不适合频繁添加和删除对象的应用场景。下面是TList类的属性和方法说明:


属性
描述
Count: Integer;
返回列表中的项目数
Items[Index: Integer]: Pointer; default
通过以0为底的索引下标直接存取列表中的项目

方法
类型
描述
Add(Item: Pointer): Integer;
函数
用来向列表中添加指针
Clear;
过程
清空列表中的项目
Delete(Index: Integer);
过程
删除列表中对应索引的项目
IndexOf(Item: Pointer): Integer;
函数
返回指针在列表中的索引
Insert(Index: Integer; Item: Pointer);
过程
将一个项目插入到列表中的指定位置
Remove(Item: Pointer): Integer;
函数
从列表中删除指针

名称
类型
描述
Capacity: Integer;
property
可以用来获取或设定列表可以容纳的指针数目
Extract(Item: Pointer): Pointer;
function
Extract 类似于Remove 可以将指针从列表中删除,不同的是返回被删除的指针。 
Exchange(Index1, Index2: Integer);
procedure
交换列表中两个指针
First: Pointer;
function
返回链表中的第一个指针
Last: Pointer;
function
返回链表中最后一个指针
Move(CurIndex NewIndex: Integer);
procedure
将指针从当前位置移动到新的位置
Pack;
procedure
从列表中删除所有nil指针
Sort(Compare: TListSortCompare);
procedure
用来对链表中的项目进行排序,可以设定Compare参数为用户定制的排序函数

TObjectList 类


TObjectList = class(TList)

public

constructor Create(AOwnsObjects: Boolean); overload;

function Remove(AObject: TObject): Integer;

function FindInstanceOf(AClass: TClass;

Integer;

property OwnsObjects: Boolean;

end;

不同于TList类,TObjectList类的Add, Remove, IndexOf, Insert等方法都需要传递TObject对象作为参数,由于有了编译期的强类型检查,使得TObjectList比TList更适合保存对象。此外TObjectList对象有OwnsObjects属性。当设定为True (默认值),同TList类不同,TObjectList对象将销毁任何从列表中删除的对象。无论是调用Delete, Remove, Clear 方法,还是释放TObjectList对象,都将销毁列表中的对象。有了TObjectList类,我们就再也不用使用循环来释放了对象。这就避免了释放链表对象时,由于忘记释放链表中的对象而导致的内存泄漏。 另外要注意的是OwnsObjects属性不会影响到Extract方法,TObjectList的Extract方法行为类似于TList,只是从列表中移除对象引用,而不会销毁对象。

TObjectList 对象还提供了一个FindInstanceOf 函数,可以返回只有指定对象类型的对象实例在列表中的索引。如果AExact 参数为True,只有指定对象类型的对象实例会被定位,如果AExact 对象为False,AClass 的子类实例也将被定位。AStartAt 参数可以用来找到列表中的多个实例,只要每次调用FindInstanceOf 函数时,将起始索引加1,就可以定位到下一个对象,直到FindInstanceOf 返回-1。下面是代码示意:

var

begin

repeat

if idx >= 0 then

until(idx
TComponentList 类

TComponentList = class(TObjectList)

public

function Remove(AComponent: TComponent): Integer;

procedure Insert(Index: Integer; AComponent: TComponent);

end;

TClassList 类

TClassList = class(TList)

function GetItems(Index: Integer): TClass;

public

function Remove(aClass: TClass): Integer;

procedure Insert(Index: Integer; aClass: TClass);

read GetItems write SetItems; default;

不同于前面两个类,这个类继承于TList的类只是将Add, Remove, IndexOf, Insert和Items 调用的参数从指针换成了TClass元类类型。

Contnrs单元还定义了其它三个类:TOrderedList, TStack和TQueue,类型定义如下:

private

protected

...

function Count: Integer;

procedure Push(AItem: Pointer);

function Peek: Pointer;

TStack = class(TOrderedList)

procedure PushItem(AItem: Pointer); override;

TQueue = class(TOrderedList)

procedure PushItem(AItem: Pointer); override;

要注意虽然TOrderedList 并不是从TList继承的,但是它在内部的实现时,使用了TList来储存指针。另外注意TOrderedList类的PushItem 过程是一个抽象过程,所以我们无法实例化 TOrderedList 类,而应该从TOrderedList继承新的类,并实现抽象的PushItem方法。TStack 和 TQueue 正是实现了PushItem抽象方法的类, 我们可以实例化TStack 和TQueue类作为后进先出的堆栈 (LIFO)和先进先出的队列(FIFO)。下面是这两个的的方法使用说明: 

· AtLeast 可以用来检查链表的大小,判断当前列表中的指针数目是否大于传递的参数值,如果为True表示列表中的项目数大于传来的参数。 

· Pop返回链表的末端指针,并将其从链表中删除。 

TObjectStack和TObjectQueue类

TObjectStack = class(TStack)

procedure Push(AObject: TObject);

function Peek: TObject;

TObjectQueue = class(TQueue)

procedure Push(AObject: TObject);

function Peek: TObject;

这两个类只是TStack和TQueue 类的简单扩展,在链表中保存的是TObject的对象引用,而不是简单的指针。

到目前为止,我们看到的容器类中保存的都是指针或者对象引用(对象引用其实也是一种指针)。

unit IntList;

uses

type

protected

procedure SetItem(Index: Integer;

public

function Extract(Item: Integer): Integer;

function IndexOf(Item: Integer): Integer;

function Last: Integer;

procedure Sort;

read GetItem write SetItem; default;

implementation

function TIntList.Add(Item: Integer): Integer;

Result := inherited Add(Pointer(Item));

function TIntList.Extract(Item: Integer): Integer;

Result := Integer(inherited Extract(Pointer(Item)));

function TIntList.First: Integer;

Result := Integer(inherited First);

function TIntList.GetItem(Index: Integer): Integer;

Result := Integer(inherited Items[Index]);

function TIntList.IndexOf(Item: Integer): Integer;

Result := inherited IndexOf(Pointer(Item));

procedure TIntList.Insert(Index, Item: Integer);

inherited Insert(Index, Pointer(Item));

function TIntList.Last: Integer;

Result := Integer(inherited Last);

function TIntList.Remove(Item: Integer): Integer;

Result := inherited Remove(Pointer(Item));

procedure TIntList.SetItem(Index: Integer;

begin

end;

begin

Result := -1

Result := 1

Result := 0;

procedure TIntList.Sort;

inherited Sort(IntListCompare);

end.

Begin Listing Two - TMyObjectList

public

end;

protected

procedure SetItems(Index: Integer; AMyObject: TMyObject);

function Add(aMyObject: TMyObject): Integer;

function Remove(aMyObject: TMyObject): Integer;

procedure Insert(Index: Integer; aMyObject: TMyObject);

read GetItems write SetItems; default;

...

function TMyObjectList.Add(AMyObject: TMyObject): Integer;

Result := inherited Add(AMyObject);

procedure TMyObjectList.DoSomething;

i: Integer;

for i := 0 to Count-1 do

end;

begin

end;

Integer;

Result := inherited IndexOf(AMyObject);

procedure TMyObjectList.Insert(Index: Integer;

begin

end;

Integer;

Result := inherited Remove(AMyObject);

procedure TMyObjectList.SetItems(Index: Integer;

begin

end;

TStrings类

要注意的是TStrings类本身包含了很多抽象的纯虚的方法,因此不能实例化后直接使用,必须从TStrings类继承一个基类实现所有的抽象的纯虚方法来进行实际的字符串列表管理。虽然TStrings类本身是一个抽象类,但是它应该说是一个使用了Template模式的模版类,提供了很多事先定义好的算法来实现添加添加、删除列表中的字符串,按下标存取列表中的字符串,对列表中的字符串进行排序,将字符串保存到流中。将每个字符串同一个对象关联起来,提供了键-值对的关联等等。

var TempList: TStrings;

TempList := TStringList.Create;

TempList.Add(‘字符串1’);

finally

end;

TStrings类的应用非常广泛,很多VCL类的属性都是TStrings类型,比如TMemo组件的Lines属性,TListBox的Items属性等等。下面将介绍一下TStrings类的常见用法。

StringList1.Strings[0] := '字符串1';

StringList1[0] := '字符串1';

if FileListBox1.Items.IndexOf('TargetFileName') > -1 ...

procedure TForm1.Button1Click(Sender: TObject);var Index: Integer;

for Index := 0 to ListBox1.Items.Count - 1 do

end;

StringList1.Insert(2, 'Three');

StringList1.AddStrings(StringList2);

Memo1.Lines.Assign(ComboBox1.Items);

同对象关联

同视图交互

可以我们在TStrings和默认的实现类TStringList的源代码中却找不到同ListBox相关的代码,那么这种界面交互是如何做到的呢?

TListBoxStrings = class(TStrings)

ListBox: TCustomListBox;



function Add(const S: string): Integer; override;

procedure Delete(Index: Integer); override;

function IndexOf(const S: string): Integer; override;

procedure Move(CurIndex, NewIndex: Integer); override;

可以看到TListBoxStrings类实现了TStrings类的所有抽象方法,同时在内部有一个ListBox的私有变量。我们再看一下TListBoxStrings的Add方法:
function TListBoxStrings.Add(const S: string): Integer;
begin
Result := -1;
if ListBox.Style in [lbVirtual, lbVirtualOwnerDraw] then exit;
Result := SendMessage(ListBox.Handle, LB_ADDSTRING, 0, Longint(PChar(S)));
if Result


可以看到TListBoxStrings在内部并没有保存添加的字符串,而是直接向Windows的原生列表盒控件发送消息实现的代码添加,而Windows的原生列表盒是一个MVC的组件,当内部的数据发生变化时,会自动改变视图显示,这就是为什么我们在设计器中输入的字符串会立刻显示在窗体列表框中的原因了。

还有一点要说明的是,Delphi的IDE只在使用Delphi的流机制保存组件到窗体设计文件DFM文件中的时,做了一些特殊的处理,能够自动保存和加载Published的TStrings类型的属性,下面就是一个ListBox储存在窗体设计文件DFM中文本形式示意(在窗体设计阶段,我们可以直接使用View As Text右键菜单命令看到下面的文本),我们可以注意到在设计时我们输入的Items的两个字符串被保存了起来:

Left = 64

Width = 145

ItemHeight = 16

'String1'

TabOrder = 1

随后如果运行程蚴保琕CL库会使用流从编译进可执行文件的DFM资源中将Items.Strings列表加载到界面上,这样就实现了设计是什么样,运行时也是什么样的所见即所得。

在实际开发过程中,我们经常会碰到类似于字典的定位操作的通过键查找相应值的操作,比如通过用户名查找用户相应的登陆密码等。在C++和Java中,标准模版库和JDK都提供了Map类来实现键-值机制,但是Delphi的VCL库却没有提供这样的类,但是TStrings类提供了一个简易的Map替代的实现,那就是Name-Value对。

var

Begin

//添加用户名-密码对

StringList1.Add(‘hubcat=bbb’);

//根据用户名hubdog查找密码

End;

THashedStringList类

Delphi6中提供的THashedStringList类没有提供任何的新的方法,只是对IndexOf和IndexOfName函数通过哈希表进行了性能优化,下面这个例子演示了TStringList和THashedStringList之间的性能差异:

unit CHash;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Inifiles;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
HashedList: THashedStringList;
DesList: TStringList;
List: TStringList;
public
{ Public declarations }
procedure Hash;
procedure Iterate;
end;
var
Form1: TForm1;

{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
I:Integer;
begin
Screen.Cursor := crHourGlass;
try
//初始化系统
for I := 0 to 5000 do
begin
HashedList.Add(IntToStr(i));
List.Add(IntToStr(i));
end;
Hash;
DesList.Clear;
Iterate;
finally
Screen.Cursor := crDefault;
end;
end;
procedure TForm1.Hash;
var
I, J: Integer;
begin
//基于哈希表的定位
for I := 3000 to 4000 do
begin
DesList.Add(IntToStr(HashedList.IndexOf(IntToStr(I))));
end;
end;
procedure TForm1.Iterate;
var
I, J: Integer;
begin
//基于遍历方式定位
for I := 3000 to 4000 do
begin
DesList.Add(IntToStr(List.IndexOf(IntToStr(I))));
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
HashedList := THashedStringList.Create;
DesList := TStringList.Create;
List := TStringList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
HashedList.Free;
DesList.Free;
List.Free;
end;

上面代码中的Hash过程,采用了新的THashedStringList类来实现的查找,而Iterate过程中使用了原来的TStringList类的IndexOfName来实现的查找。采用GpProfile(注:GpProfile的用法参见工具篇的性能分析工具GpProfile章节)对两个过程进行了性能比较后,从下图可以看到Hash执行同样查找动作只用了0.7%的时间,而Iterate方法则用了99.3%的时间,可以看到在字符串列表项目数在几千的数量级别时,基于哈希表的查询速度是原有方法的100多倍。
不过要说明的是,THashedStringList同TStringList类相比,虽然查找的速度
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值