在Delphi中实现类型安全的容器
by [email protected],2004.09.12
Delphi中的容器和算法实在太缺乏了,又存在很多不一致,使用也很不方便。在构造一些容器和算法的时候,总是怀念C++的泛型和STL;所以就尝试在Delphi中编写泛型代码;其它现有的Deplhi容器和算法库实现中,主要的实现途径有利用Delphi中的array of const(相当于弱类型,而且对结构的支持差,如Decal);或者建立一套类体系作为容器中的元素(主要使用虚函数机制,如:左轻侯有篇文章也谈到过; 一般简单类型需要做打包拆包);还有的实现是针对TObject、IInterface、String等做多套代码实现(如:DCL库); 但他们相对于C++的STL来说缺陷也很明显,类型不安全,速度慢,代码重复; DGL库克服了这些问题,能够用类型安全的方式支持所有基本类型、指针(包括类的指针)、Interface、结构(record)、Object结构(Delphi中已经不推荐使用)、类成员函数指针、类(class)的值语义(一般Delphi中不习惯使用类的值语义,所以不建议使用)等其它用户自定义类型,并且类型安全(速度当然没有问题啦), 没有重复代码!
库现在包括以下组件:
<object>
_IIterator 容器迭代器
_IMapIterator Map迭代器
(PointerBox函数可以将原生指针包装成一个与库兼容的迭代器)<接口interface>
_ICollection 容器接口
_ISerialContainer 序列容器的接口
_IVector 向量接口(容器的一种)
_IList 链表接口(容器的一种)
_IDeque 队列接口(容器的一种)
_IStack 堆栈接口(一种容器配接器)
_IQueue 双端队列接口(一种容器配接器)
_IPriorityQueue 优先级队列_ISet Set接口
_IMultiSet MultiSet接口
_IMap Map接口
_IMultiMap MultiMap接口 <类class>
_TAlgorithms 算法类(包括:拷贝、替换、删除、排序、搜索等算法)_TAbstractContainer 容器虚基类
_TAbstractSerialContainer 序列容器类的虚基类
_TAbstractSet Set(MultiSet)的抽象虚基类
_TAbstractMap Map(MultiMap)的抽象虚基类
_TVector 向量实现类
_TList 链表List的一个实现
_TDeque 队列Deque的一个实现
_TStack 堆栈Stack
_TQueue 双端队列Queue (_THashTableBase Hash表(库内部使用))
_THashSet 用Hash表实现的Set
_THashMultiSet 用Hash表实现的MultiSet
_THashMap 用Hash表实现的Map
_THashMultiMap 用Hash表实现的MultiMap
by [email protected],2004.09.12
Delphi中的容器和算法实在太缺乏了,又存在很多不一致,使用也很不方便。在构造一些容器和算法的时候,总是怀念C++的泛型和STL;所以就尝试在Delphi中编写泛型代码;其它现有的Deplhi容器和算法库实现中,主要的实现途径有利用Delphi中的array of const(相当于弱类型,而且对结构的支持差,如Decal);或者建立一套类体系作为容器中的元素(主要使用虚函数机制,如:左轻侯有篇文章也谈到过; 一般简单类型需要做打包拆包);还有的实现是针对TObject、IInterface、String等做多套代码实现(如:DCL库); 但他们相对于C++的STL来说缺陷也很明显,类型不安全,速度慢,代码重复; DGL库克服了这些问题,能够用类型安全的方式支持所有基本类型、指针(包括类的指针)、Interface、结构(record)、Object结构(Delphi中已经不推荐使用)、类成员函数指针、类(class)的值语义(一般Delphi中不习惯使用类的值语义,所以不建议使用)等其它用户自定义类型,并且类型安全(速度当然没有问题啦), 没有重复代码!
库现在包括以下组件:
<object>
_IIterator 容器迭代器
_IMapIterator Map迭代器
(PointerBox函数可以将原生指针包装成一个与库兼容的迭代器)<接口interface>
_ICollection 容器接口
_ISerialContainer 序列容器的接口
_IVector 向量接口(容器的一种)
_IList 链表接口(容器的一种)
_IDeque 队列接口(容器的一种)
_IStack 堆栈接口(一种容器配接器)
_IQueue 双端队列接口(一种容器配接器)
_IPriorityQueue 优先级队列_ISet Set接口
_IMultiSet MultiSet接口
_IMap Map接口
_IMultiMap MultiMap接口 <类class>
_TAlgorithms 算法类(包括:拷贝、替换、删除、排序、搜索等算法)_TAbstractContainer 容器虚基类
_TAbstractSerialContainer 序列容器类的虚基类
_TAbstractSet Set(MultiSet)的抽象虚基类
_TAbstractMap Map(MultiMap)的抽象虚基类
_TVector 向量实现类
_TList 链表List的一个实现
_TDeque 队列Deque的一个实现
_TStack 堆栈Stack
_TQueue 双端队列Queue (_THashTableBase Hash表(库内部使用))
_THashSet 用Hash表实现的Set
_THashMultiSet 用Hash表实现的MultiSet
_THashMap 用Hash表实现的Map
_THashMultiMap 用Hash表实现的MultiMap
http://www.tomore.com/1/41811.html我使用这个库两年了,用它构造的程序可能已经有上百万人在使用:)这个库的特色在于类型安全\快速\容易使用,库的结构大量借鉴了STL,当然结合了Delphi本身的特点;代码支持Delphi7和TurboDelphi编译环境(其他环境没有测试过)
procedure _TStack.Assign(const ItBegin, ItEnd: _IIterator);
begin
self.FBaseContainer.Assign(ItBegin, ItEnd);
end;如在Delphi IDE中,直接用CTRL+Shift+上下箭头,即可切换声明与实现的代码,
而我却找了半天,还没找到TStack的声明,晕
估计看你代码的同事,头大不已,或者说你同事也是搞C的。我只觉得我是Delphi程序员,我会按VCL标准写法去写代码,如果写VC代码会用标准MFC代码写,不过另搞一套,搞的大家郁闷那。
JCL的DCL针对TObject、IInterface、String三种类型分别写了三套代码实现
1。也就是代码重复了三次,而且我在使用它的一个版本(2004年的)的时候发现Hash表的实现有一个错误(也有人报告了同样的问题),然后修改了三处; 2。支持三种类型也就是它不能支持其他类型,比如Byte类型,你会说可以用TObject指针强制类型转换当作Byte用,但内存使用变成4倍,用户使用也很繁琐; 而且如果我想在容器中储存Record,那就很难了; 严格的说,DCL并不是“泛型”库;
————————
而DGL库内部的代码编写是完全“泛型”的,用户使用库界面(也就是易用性)和DCL一致
所以按照STL的思想代码,使用手工扩展代码的方式来完成“范型”的任务;我相信源代码的可阅读还是不错的(如果你看过Decal的底层实现的话:)
我自己写的,但并不是闭门造车,你可以和其他几个实现对比,不管是编码质量\还是代码效率\或者易用性,都欢迎批评; 我的工作中需要的容器和算法比较多,对效率要求也非常高,所以就有了DGL;
最近在构造一个新的版本,与以前的兼容,但速度会更快;我想使用新的语法特性inline来提高性能,目标是使库中的Vector接近数组的效率(基本达到),迭代器的效率接近指针的效率(正在考虑方案);这样的话库的包装优势将更加明显;
你的观点和意见都比较集中于可读性,那么我想知道你有没有读过STL的源代码(SGI的那个实现比较好读)? 或者读Decal的实现(我觉得是个恐怖的经历,而且Decal其实只支持一种类型,就是delphi中的TVarRec弱类型) DCL的源代码可读性很不错,就是功能上弱了一些,所有代码一式三份:) ; 而我想要支持Byte流的容器,想要操作一个特殊数据区中的算法,我想要支持函数映射表的map,支持我的大量TChatData记录的算法,我想要一个快速高性能的网络消息栈... DCL都不能给我; (这也是我从头构造DGL的原因) 把STL中的功能做成Dll来调用,可行,但工作量太大了,而且易用性会受到很大的影响;不知道有多少人这样用:)
JCL太庞大了,无所不包(也就想拆出一小块来用也不容易:);要想使用它和测试它然后用于商业软件中感觉就像一个恐龙(这是我从不用它的个人意见而已:) 有一次我仅翻遍这个库的源代码文件就用了2个小时(就像翻书那样翻,几秒一个文件)!
DGL库的源文件组织上有点像C\C++ (没有办法,只能这样), 但库的可读性个人认为还是很不错的,而且我也不会要求使用DGL的Delphi开发人员都来阅读DGL,直接在Delphi中使用那些DGL_XXX.pas文件就可以了;
泛型和STL主要的功能就是实现数组(LIST),队列(QUEUE),栈(Stack),散列表(HASH),还有什么?应该还有树,图吧,感觉比较少用,这两个。差不多之前学的数据结构都用到了。
容器,集合感觉可以用上面的东西扩展出来,其它的也类型。
新的下载地址:http://cosoft.org.cn/projects/dgl/在inline的支持下,其中的Vector的效率甚至可以和Delphi中的array of type想抗衡(如果编译器优化的好,甚至会生成相同的汇编码);
“另:我看很多LIST,QUEUE, Statck的代码,其中部分是尽量减少内存的New, Dispose处理,增加对内存缓冲的处理,这方面在DGL还没,下次加加,这样效率会好些。” 恩,你的意见不错; DGL中Vector和Deque都有了类似的结构;相应的QUEUE和Statck也有了,因为他们内部就是使用Deque来实现的;List可以考虑增加一个自己的专用的内存管理器来提高效率,但在Delphi中这个(从现在实际的性能测试来看)必要性并不强,如果程序中使用FastMM(一个替代Delphi内存管理的第三方库,新版Delphi使用了它),好像必要性就更不强了;我会做一次性能对比来决定是否在List的实现中替换内存管理方案
“哦,问一下:泛型和STL主要的功能就是实现数组(LIST),队列(QUEUE),栈(Stack),散列表(HASH),还有什么?应该还有树,图吧,感觉比较少用,这两个。差不多之前学的数据结构都用到了。
容器,集合感觉可以用上面的东西扩展出来,其它的也类型。” “泛型”是一种编程范型(其他比如过程式编程、面向对象、函数Functor式编程),在C++中的泛型使用模板(template)来实现(java中使用编译器支持的弱类型);
“STL”是C++的标准模板库,库主要实现了泛型的容器、算法等;
DGL在Delphi中主要使用手工代码扩展的技术来实现泛型的容器、算法。
ezdsl也并不是泛型库,也就是支持的类型有限,比如hash表只支持string做key映射到pointer;ezdsl中有些代码写的比较死,使用了很多汇编,比如TStringStack的实现就很不优雅,只能支持255个字符的串,效率也很低库里面有很多不安全或者hack的代码,如:
procedure TStringStack.Push(const S : string);
var
NewS : string;
begin
NewS := S; //为了增加引用计数
Stack.Push(pointer(NewS));
pointer(NewS) := nil;
end;
procedure EZLongStrDisposeData(aData : pointer);
begin
string(aData) := '';//这里释放引用计数
end;string在Delphi中现在的实现利用了写时复制(copy on write)加引用计数的手法来实现的,其实这是一个很有争议的实现方案,特别是现在有很多的多线程程序和多核CPU流行的背景下;ezdsl利用Delphi的现有string实现方案来hack实现是比较危险的;
type
{
icontainer = interface
[xxx...]
...
end;
tdata_carrier = class
public
function compare(a: tobject): integer; virtual; abstract;
end;
tdata_carrier_class = class of tdata_carrier;
tabstractcontainer_class = class of Ezdslbse.TAbstractContainer;
tcontainerbase = class(tinrefacedobject, icontainer)
protected
fcontainer: tabstractcontainer;
protected
class function get_container_class: tabstractcontainer_class; virtual; abstract;
public
constractor create(const aOwnsData: boolean); virtual;
constractor create2(const aOwnsData: boolean; const aCompareFunc: tlistsortcompare); virtual;
end;
function data_carrier_compare(a, b: pointer): integer;
var
x, y: tdata_carrier;
begin
x := tdata_carrier(a);
y := tdata_carrier(b);
result := x.compare(y);
end;
procedure dispose_data_object(p: pointer):
begin
tobject(p).free();
end;
constructor tcontainerbase.create(const aOwnsData: boolean);
begin
fcontainer := get_container_class().create(aOwnsData);
fcontainer.compare := data_carrier_compare;
fcontainer.disposedata := dispose_data_object;
end;
constractor tcontainerbase.create2(const aOwnsData: boolean; const aCompareFunc: tlistsortcompare);
begin
fcontainer := get_container_class().create(aOwnsData);
fcontainer.compare := aCompareFunc;
fcontainer.disposedata := dispose_data_object;
end;
}
type
tstring_carrier = class(t_data_carrier)
protected
function compare(a: tobject): integer; override;
public
value: string;
end;function tstring_carrier.compare (a: tobject): integer;
var
x: tstring_carrier;
begin
x := tstring_carrier(a); result := comparestring(value, x.value);
end;
type
//特化
istringstack = interface(icontainer)
[xxxx-xxxxx...]
procedure push(const aValue: string);
function pop(): string;
end; tstringstack = class(tcontainerbase, istringstack)
private
fstack: tstack;
protected
...
class function get_container_class: tabstractcontainer_class; override;
procedure push(const aValue: string);
function pop(): string;
end;procedure tstringstack.push(const avalue: string);
var
carrier: tstringcarrier;
begin
carrier := tstringcarrier.create();
carrier.value := avalue;
fstack.push(carrier);
end;...
要说易用性,你用一下DGL就可以知道了; 若果你知道DCL库(我见到的其他几个算法库中最易用的库),那么DGL库的使用方法和它非常接近,如果你熟悉STL,那就更容易上手;
Delphi虽然不直接支持范型,带这并没有带来多少底层实现的复杂度,DGL库的实现并不比STL库的实现难读; 你可以看一下DGL,自己运行里面的示例;它的速度足够让你满意,如果把C++中的STL看作利用tamplate来扩展代码得到类型安全和快速;那么DGL可以看作利用了Include直接扩展代码来得到等价的类型安全和速度!
DGL中那些使用_T开头的类或者其他用下划线前缀的定义,代表的是“库内部”使用的意思,不希望库的使用者直接使用这些定义;
Delphi7 Compile DGL lib============================================================================
Container:PushBack Next Visite Random Visite Insert At Middle PushFront
TVector 0.010 us 0.008 us 0.102 us 34400.000 us 78000.000 us
TDeque 0.009 us 0.055 us 0.155 us 143800.000 us 0.010 us
TList 0.067 us 0.022 us 15310.000 us 0.073 us 0.053 us
IVector 0.014 us 0.018 us 0.156 us 34400.000 us 78200.000 us
IDeque 0.013 us 0.070 us 0.235 us 153000.000 us 0.016 us
IList 0.073 us 0.022 us 15160.000 us 0.078 us 0.056 us
Container: Insert Find Next Visite
IMap 0.274 us 0.797 us 0.048 us
IMultiMap 0.298 us 0.899 us 0.067 us
ISet 0.272 us 0.805 us 0.044 us
IMultiSet 0.253 us 0.805 us 0.043 us
====================================================================================Delphi7=============================================================================
Container:PushBack Next Visite Random Visite Insert At Middle PushFront
arrayof 0.168 us 0.006 us 0.086 us 40600.000 us 121800.000 us
TList 0.023 us 0.013 us 0.164 us 28000.000 us 65600.000 us
====================================================================================STL : VC6 Compile===================================================================
Container:PushBack Next Visite Random Visite Insert At Middle PushFront
vector 0.049 us 0.005 us 0.032 us 34400 us 68800 us
deque 0.013 us 0.016 us 0.055 us 212400 us 0.013 us
list 0.142 us 0.022 us 18800 us 0.161 us 0.159 us Container: Insert Find Next Visite
map 0.408 us 0.277 us 0.054 us
set 0.405 us 0.278 us 0.048 us
multimap 0.430 us 0.277 us 0.055 us
multiset 0.405 us 0.278 us 0.048 us
====================================================================================DCL lib===============================================================================
Container: PushBack Next Visite Random Visite Insert At Middle PushFront
TArrayList 0.019 us 0.029 us 0.164 us 25000.000 us 65600.000 us
TVector 0.014 us 0.025 us 0.164 us 25000.000 us 62400.000 us
TLinkedList 0.050 us 0.023 us 5780.000 us 0.056 us 0.086 us
Container: Insert Find Next Visite
TBinaryTree 0.470 us 1.047 us 0.086 us
THashMap 0.422 us 0.788 us 0.084 us(copy Values to TArrayList,it is not ture test!)
THashSet 0.485 us 0.588 us "Container.First" is O(N*N), is bad,can not test! ps: THashXXX.Create()
for I := 0 to FCapacity - 1 do
SetLength(FBuckets[I].Entries, 64);) //is bad design, slow and waste memory!ps: THashXXX //is bad design for dynamic sizeps: TLinkedList.Clear() ERROR !!!
procedure TLinkedList.Clear;
.....
//ERROR , must add line: FStart:=nil;
end;
=====================================================================================DeCal lab============================================================================
Container: PushBack Next Visite Random Visite Insert At Middle PushFront
DArray 0.206 us 0.184 us 0.305 us 53200.000 us 128000.000 us
DList 0.147 us 0.127 us 13109.000 us 0.167 us 0.141 us
Container: Insert Find Next Visite
DSet 2.117 us 6.594 us 0.117 us
DMultiSet 2.000 us 5.532 us 0.117 us
DMap 1.961 us 4.969 us 0.115 us
DMultiMap 1.875 us 5.234 us 0.149 us DHashSet 22.500 us 58.740 us 0.833 us
DMultiHashSet 0.372 us 5465.000 us 0.788 us
DHashMap 29.680 us 62.820 us 0.727 us
DMultiHashMap 0.681 us 2970.000 us 0.786 us
=====================================================================================EZDSL lab============================================================================
Container: PushBack Next Visite Random Visite Insert At Middle PushFront
TEZCollection is maxsize is N=10922*92 for test! it is very small for test;
TEZCollection 0.078 us 0.077 us 6.233 us 0.388 us 0.588 us
TDList 0.175 us 0.028 us 7050.000 us 0.170 us 0.150 us
TLinkList 0.155 us 0.033 us 15650.000 us 0.167 us 0.147 us
Container: Push Pop
TQueue 0.139 us 0.150 us
TStack 0.142 us 0.145 us
Container: Insert Find Next Visite
THashTable 7.580 us 1.720 us (not find way to test!)
=====================================================================================
奇怪的是array(Delphi自己的)\Tvector和TDeque的随机寻址速度比STL的慢很多! 可能是随机数发生器的调用问题;什么时候测试一下SGI的STL看看性能怎样:)
增加了SGI实现的STL的性能测试数据,果然很强!
DGL lib=========================================================================
Container: PushBack Next Visite Random Visite Insert At Middle PushFront
TVector 0.010 us 0.008 us 0.102 us 34400.000 us 78000.000 us
TDeque 0.009 us 0.055 us 0.155 us 143800.000 us 0.010 us
TList 0.067 us 0.022 us 15310.000 us 0.073 us 0.053 us
IVector 0.014 us 0.018 us 0.156 us 34400.000 us 78200.000 us
IDeque 0.013 us 0.070 us 0.235 us 153000.000 us 0.016 us
IList 0.073 us 0.022 us 15160.000 us 0.078 us 0.056 us
Container: Insert Find Next Visite
IMap 0.274 us 0.797 us 0.048 us
IMultiMap 0.298 us 0.899 us 0.067 us
ISet 0.272 us 0.805 us 0.044 us
IMultiSet 0.253 us 0.805 us 0.043 us
================================================================================Delphi7=========================================================================
Container: PushBack Next Visite Random Visite Insert At Middle PushFront
array 0.168 us 0.006 us 0.086 us 40600.000 us 121800.000 us
TList 0.023 us 0.013 us 0.164 us 28000.000 us 65600.000 us
================================================================================STL : VC6 Compile===============================================================
Container: PushBack Next Visite Random Visite Insert At Middle PushFront
vector 0.049 us 0.005 us 0.032 us 34400 us 68800 us
deque 0.013 us 0.016 us 0.055 us 212400 us 0.013 us
list 0.142 us 0.022 us 18800 us 0.161 us 0.159 usContainer: Insert Find Next Visite
map 0.424 us 3.297 us 0.047 us
set 0.431 us 3.289 us 0.048 us
multimap 0.433 us 3.298 us 0.047 us
multiset 0.431 us 3.288 us 0.048 us
(SGI)STL : DEV-C++4.98 (GCC max optimize) Compile===============================
Container: PushBack Next Visite Random Visite Insert At Middle PushFront
vector 0.016 us 0.005 us 0.032 us 34400 us 56200 us
deque 0.012 us 0.008 us 0.133 us 875000 us 0.013 us
list 0.044 us 0.016 us 12400 us 0.052 us 0.036 usContainer: Insert Find Next Visite
map 0.458 us 3.352 us 0.035 us
set 0.445 us 3.336 us 0.036 us
multimap 0.403 us 3.236 us 0.035 us
multiset 0.413 us 3.222 us 0.036 us hash_map 0.155 us 0.705 us 0.043 us
hash_set 0.147 us 0.653 us 0.043 us
hash_multimap 0.141 us 0.706 us 0.044 us
hash_multiset 0.138 us 0.652 us 0.043 us
================================================================================
安装DGL的方法是把DGL源代码所在的目录设置到Delphi的搜索路径中;
DGL设计成了扁平风格的类框架,这是最容易实用的吧,不用记各种各样的函数名:);
你直接使用那些DGL_XX.pas单元就可以了,不用关心那些底层实现:) 比如直接在项目中引用DGL_Pointer.pas 使用其中的TPointerDeque类;
var
piDeque : IPointerDeque;
begin
piDeque :=TPointerDeque.Create();
piDeque.PushBack(Pointer(2));
piDeque.PushFront(Pointer(3));
Assert(piDeque.iterms[0]=Pointer(3));
Assert(piDeque.iterms[1]=Pointer(2));
//not need free piDeque,it auto free;
end;
是啊,“语法糖”对于编写简单易读的代码还是很有用的;
我想只要有了这层“范型”语法糖,我的DGL将很容易移植到泛型实现(代码已经按这个写好了:),并且也更易用;
今天会发布一个新版本,主要改进了Deque的迭代器的内部实现;DGL的性能可以和C++中SGI实现的STL进行对比(对比数据可以看我的blog);(算法部分还没有做评估)
DGL的Demo和Doc比较少,如果熟悉STL的话到没有多少问题;
DGL下载包中现在可以看作例子就仅仅是单元测试和性能测试程序;我随手写了几个DGL的Demo,一个函数是一个:///////////////////////////...implementation
uses
DGL_Pointer,DGL_Byte,DGL_Integer,_DGLMap_String_Integer,DGL_String;{$R *.dfm}procedure TForm1.btn_IIntVectorClick(Sender: TObject);
var
intVector : IIntVector; //interface type ; Vector use as delphi's array;
i,Sum : integer;
begin
intVector :=TIntVector.Create; for i:=0 to 100-1 do
intVector.PushBack(i);
Assert(intVector.Size()=100); Sum:=0;
for i:=0 to intVector.Size()-1 do
Sum:=Sum+intVector.Items[i];
Assert(Sum=(0+99)*100 div 2); // intVector is interface type not need free;
end;procedure TForm1.btn_TIntVectorClick(Sender: TObject);
var
intVector : TIntVector; //class type
i,Sum : integer;
begin
intVector :=TIntVector.Create;
try
for i:=0 to 100-1 do
intVector.PushBack(i);
Assert(intVector.Size()=100); Sum:=0;
for i:=0 to intVector.Size()-1 do
Sum:=Sum+intVector.Items[i];
Assert(Sum=(0+99)*100 div 2); finally
intVector.Free; //intVector is class type;
//use class type is maybe faster than interface type;
//recommend priority use DGL container as interface by most time;
end;end;procedure TForm1.btn_IIntDequeClick(Sender: TObject);
var
intDeque : IIntDeque;
i,Sum : integer;
begin
intDeque :=TIntDeque.Create; for i:=0 to 100-1 do
intDeque.PushBack(i);
Assert(intDeque.Back()=99);Assert(intDeque.Front()=0);
Assert(intDeque.Size()=100); for i:=0 to 100-1 do
intDeque.PushFront(i); //Deque's PushFront and PushBack is the same fast;
Assert(intDeque.Back()=99);Assert(intDeque.Front()=99);
Assert(intDeque.Size()=200); Sum:=0;
for i:=0 to intDeque.Size()-1 do
Sum:=Sum+intDeque.Items[i];
Assert(Sum=((0+99)*100 div 2)*2);
end;procedure TForm1.btn_IPointerListClick(Sender: TObject);
var
piList : IPointerList;
it : IPointerIterator;
i,Sum : integer;
begin
piList :=TPointerList.Create; for i:=0 to 100-1 do
piList.PushBack(Pointer(i));
for i:=0 to 100-1 do
piList.PushFront(Pointer(i)); it:=piList.ItBegin.Clone(100); //it is pointer to piList's middle;
for i:=0 to 100-1 do
piList.Insert(it,Pointer(i));
//List's PushFront and PushBack and insert at any pos is the same fast;
Assert(piList.Size()=300); Sum:=0;
it:=piList.ItBegin; //it is pointer to piList's first;
for i:=0 to piList.Size()-1 do
begin
Sum:=Sum+integer(it.Value);
it.Next;
end;
Assert(it.IsEqual(piList.ItEnd));
Assert(Sum=((0+99)*100 div 2)*3);
end;
procedure TForm1.btn_IIntSerialContainerClick(Sender: TObject);
procedure SetVaues(const Container:IByteSerialContainer);
var
i : integer;
begin
for i:=0 to 100-1 do
Container.PushBack(i);
end; procedure CheckSum(const Container:IByteSerialContainer);
var
it : IByteIterator;
Sum : integer;
begin
it:=Container.ItBegin;
Sum :=0;
while (not it.IsEqual(Container.ItEnd)) do
begin
sum:=sum+it.Value;
it.Next;
end;
Assert(Sum=(0+99)*100 div 2);
end;var
BVector : IByteVector;
BDeque : IByteDeque;
BList : IByteList;
it: IByteIterator;
begin
BVector :=TByteVector.Create;
BDeque :=TByteDeque.Create;
BList :=TByteList.Create; SetVaues(BVector);
CheckSum(BVector);
SetVaues(BDeque);
CheckSum(BDeque);
SetVaues(BList);
CheckSum(BList);
end;
procedure TForm1.btn_IPointerSetClick(Sender: TObject);
var
piSet : IPointerSet;
it : IPointerIterator;
i,Sum : integer;
begin
piSet :=TPointerHashSet.Create; for i:=0 to 200-1 do
piSet.Insert(Pointer(i div 2)); //values is [0,1,..99]
Assert(piSet.size()=100); Sum:=0;
it:=piSet.ItBegin;
for i:=0 to piSet.Size-1 do
begin
inc(Sum,integer(it.Value));
it.Next;
end;
Assert(Sum=((0+99)*100 div 2)); for i:=0 to 100-1 do
begin
it:=piSet.Find(Pointer(i));
Assert(not it.IsEqual(piSet.ItEnd));
Assert(it.Value=Pointer(i));
end; for i:=0 to 100-1 do
piSet.EraseValue(Pointer(i)); //== piSet.Erase(piSet.Find(Pointer(i)));
Assert(piSet.IsEmpty());
Assert(piSet.Size()=0);end;procedure TForm1.btn_IPointerMultiSetClick(Sender: TObject);
var
piMSet : IPointerMultiSet;
it,itt : IPointerIterator;
i,Sum : integer;
begin
piMSet :=TPointerHashMultiSet.Create; for i:=0 to 200-1 do
piMSet.Insert(Pointer(i div 2)); //values is [0,0,1,1..99,99]
Assert(piMSet.size()=200); Sum:=0;
it:=piMSet.ItBegin;
for i:=0 to piMSet.Size-1 do
begin
inc(Sum,integer(it.Value));
it.Next;
end;
Assert(Sum=((0+99)*100 div 2)*2); for i:=0 to 100-1 do
begin
it:=piMSet.Find(Pointer(i));
Assert(not it.IsEqual(piMSet.ItEnd));
Assert(it.Value=Pointer(i)); Assert(piMSet.Count(Pointer(i))=2); it:=piMSet.LowerBound(Pointer(i));
Assert(it.Value=Pointer(i));
itt:=piMSet.UpperBound(Pointer(i));
Assert(it.Distance(itt)=2);
end; Assert(piMSet.Size()=200);
for i:=0 to 100-1 do
piMSet.Erase(piMSet.Find(Pointer(i)));
Assert(piMSet.Size()=100);
for i:=0 to 100-1 do
piMSet.Erase(piMSet.Find(Pointer(i)));
Assert(piMSet.Size()=0);end;procedure TForm1.btn_IStrIntMapClick(Sender: TObject);
var
StrIntMap : IStrIntMap;
i,Sum : integer;
it: IStrIntMapIterator;
begin
StrIntMap :=TStrIntHashMap.Create;
StrIntMap.Insert('a1',1);
StrIntMap.Insert('a2',2);
StrIntMap.Insert('a3',3);
Assert(StrIntMap.Items['a1']=1);
Assert(StrIntMap.Items['a2']=2);
Assert(StrIntMap.Items['a3']=3);
StrIntMap.Clear(); for i:=0 to 100 -1 do
StrIntMap.Items[inttostr(i)]:=i*3; //== StrIntMap.Insert(inttostr(i),i*3);
Assert(StrIntMap.size()=100); Sum:=0;
for i:=0 to StrIntMap.Size-1 do
inc(Sum,StrIntMap.Items[inttostr(i)]);
Assert(Sum=((0+99)*100 div 2)*3); for i:=0 to 100-1 do
begin
it:=StrIntMap.Find(inttostr(i));
Assert(not it.IsEqual(StrIntMap.ItEnd));
Assert(it.Value=i*3);
Assert(strToint(it.Key)=i);
end; for i:=0 to 100-1 do
StrIntMap.Erase(StrIntMap.Find(inttostr(i)));
Assert(StrIntMap.Size()=0);
end;procedure TForm1.btn_IStrMultiMapClick(Sender: TObject);
var
StrMMap : IStrMultiMap;
i,Sum : integer;
it,itt: IStrMapIterator;
begin
StrMMap :=TStrHashMultiMap.Create;
StrMMap.Insert('a1','1');
StrMMap.Insert('a2','2');
StrMMap.Insert('a3','3');
Assert(StrMMap.Items['a1']='1');
Assert(StrMMap.Items['a2']='2');
Assert(StrMMap.Items['a3']='3'); StrMMap.Insert('a1','4');
Assert((StrMMap.Items['a1']='1') or (StrMMap.Items['a1']='4'));
Assert(StrMMap.Count('a1')=2);
it:=StrMMap.LowerBound('a1');
Assert((strtoint(it.Value)+strtoint(it.NextValue[1]))=(1+4)); ///////////////////////// StrMMap.Clear(); for i:=0 to 200 -1 do
StrMMap.Items[inttostr(i div 2)]:=inttostr(i div 2); //== StrMMap.Insert(inttostr(i),inttostr(i*3));
Assert(StrMMap.size()=200); Sum:=0;
for i:=0 to StrMMap.Size-1 do
inc(Sum,strtoint(StrMMap.Items[inttostr(i div 2)]));
Assert(Sum=((0+99)*100 div 2)*2); for i:=0 to 100-1 do
begin
it:=StrMMap.Find(inttostr(i));
Assert(not it.IsEqual(StrMMap.ItEnd));
Assert(strtoint(it.Value)=i);
Assert(strToint(it.Key)=i); Assert(StrMMap.Count(inttostr(i))=2); it:=StrMMap.LowerBound(inttostr(i));
Assert(it.Value=inttostr(i));
Assert(it.Key=inttostr(i));
itt:=StrMMap.UpperBound(inttostr(i));
Assert(it.Distance(itt)=2);
end; for i:=0 to 100-1 do
StrMMap.Erase(StrMMap.Find(inttostr(i)));
Assert(StrMMap.Size()=100);
for i:=0 to 100-1 do
StrMMap.Erase(StrMMap.Find(inttostr(i)));
Assert(StrMMap.Size()=0);end;var
AgSum : integer;
procedure SumAValue(const Value: Byte);
begin
inc(AgSum,Value);
end;
var
AgStr: string;
procedure TForm1.btn_IByteIteratorClick(Sender: TObject);
function ValuesAsStr(const it0,it1:IByteIterator):string;
var
it : IByteIterator;
begin
result:='';
it:=it0;
while not it.IsEqual(it1) do
begin
result:=result+inttostr(it.Value)+' ';
it.Next;
end;
end;
var
BContainer : IByteContainer;
it0,it1: IByteIterator;
i,sum : integer;
begin
BContainer :=TByteVector.Create(100);
//BContainer :=TByteDeque.Create(100);
//BContainer :=TByteList.Create(100);
Assert(BContainer.Size()=100); it0:=BContainer.ItBegin();
it1:=BContainer.ItEnd();
while not it0.IsEqual(it1) do
begin
it0.Value:=random(256);
it0.Next;
end; it0:=BContainer.ItBegin();
it1:=BContainer.ItEnd();
Sum:=0;
while not it0.IsEqual(it1) do
begin
inc(sum,it0.Value);
it0.Next;
end; AgSum:=0;
TByteAlgorithms.ForEach(BContainer.ItBegin(),BContainer.ItEnd(),@SumAValue);
Assert(AgSum=Sum); AgStr:=ValuesAsStr(BContainer.ItBegin(),BContainer.ItEnd());
TByteAlgorithms.Sort(BContainer.ItBegin(),BContainer.ItEnd());
AgStr:=ValuesAsStr(BContainer.ItBegin(),BContainer.ItEnd());
Assert(TByteAlgorithms.IsSorted(BContainer.ItBegin(),BContainer.ItEnd()));end;
谢谢了如
DECAL 数据结构与算法泛型类库
http://sourceforge.net/projects/decal/
DGL的易用性很好,你可以看看上面那些Demo :) 对于熟悉STL的人来说,更加容易上手;
DGL是类型安全的,库内部使用泛型思想编码,支持任何类型(基本类型\String\Record\Interface\Class等),而且可以很容易支持自定义类型(手工代码扩展,或者叫具现化);DECAL其实只支持Delphi的变体类型;
DGL的实现是高效的,你甚至可以拿DGL和C++中有名的SGI实现的STL对比性能;
(DGL的可读性很好)
(DGL是可以移植的,今天我就顺利用FreePascal编译了DGL的代码) (可以看我的Blog: http://blog.csdn.net/HouSisong )
Delphi现在不支持泛型语法,但并不妨碍泛型思想在Delphi中的使用;Delphi中一般实现“泛型”的方案有弱类型,就是利用const array和Variant变体类型,Decal(SDL)就使用了这个方案; 还可以利用类体系来完成,定义一个元素基类,然后所有元素都是这个基类的子类,库写一套代码支持基类;这种库对基本类型支持不好,需要做打包(放到容器之前)和拆包(从容器取出后)(我的库的第一个尝试方案,java的泛型本质上也是这个方案); DCL库的实现是写了3套一样的代码来分别支持string\interface\TObject类型;
DGL库从本质上来讲和STL是最像的,不管缺点还是优点都很像,比如 高效的代码 和 代码膨胀问题; 如果把STL的使用看作利用tamplate作代码展开,不同的类型扩展出一套新的代码,然后编译器编译;那么DGL实现的原理是利用$include文件的方式作代码展开(和STL一样,不同的类型生成不同的代码让编译器编译)
大多数时候直接使用DGL已经扩展好的代码(就是那些DGL_"ValueType".pas文件)就可以了(上面的Demo都是这种方式),这就如STL使用中的预先具现化: typedef std::vector<int> TIntVector; typedef std::vector<string> TStringVector;
对于自定义类型,比如Record,在STL中可以直接用std::list<YouType> ,在DGL中没有这么方便(缺乏编译器支持的语法糖);要完成这个任务,在DGL中需要用户新开一个pas文件来完成具现化,具现化的方法可以借鉴那些DGL_"ValueType".pas文件
我对比过DECAL与DGL的代码,我个人感觉
**可读性,对于DELPHI程序DECAL可读性较高,这一点前面好多人有提到过..
**性能.是此类产品的生命力所在.此类产品性能我还没完整对比过~~~~不过很关注~~~
**至于可移植,DECAL针对Delphi写的.移植性差~~
Decal的代码都在一个pas单元,分成上面的定义部分和实现部分,比较符合Delphi的习惯;DGL的代码按功能和类分成了多个文件实现,而且使用了C\C++的文件组织方式(这样组织是因为DGL的代码需要支持任何可能的类型);所以开始有点不习惯:)
Decal比DGL慢很多;各个库的性能对比测试数据(今天做了更新)可以参见:http://blog.csdn.net/housisong/archive/2006/10/12/1331993.aspx
谢谢关注; 不过说得也太.... 哈哈 (宝兰才顾不上Delphi呢)
DGL成型于2004年,最近对库的结构和一些实现进行了一些优化,容器部分差不多完成; 算法部分以前完成了大部分常用的算法(很多时候都是我用到了才添加上:),还需要做一次增加和优化(使其至少达到STL的标准);今天计划给DGL增加一个单向链表容器;
一个人的精力有限;