-
Notifications
You must be signed in to change notification settings - Fork 36
可变类型的 List
尽管 Delphi 的 VCL/CLX 和 Free Pascal 的 FCL 本身提供了一些 List 类型(例如:TList,TInterfaceList,TStringList 等),但这些 List 类型在处理混合类型数据时并不方便。
比如 TList 中的元素都是无类型指针,虽然指针可以指向任何类型,但是却无法在同一个 TList 对象中同时存放指向几种不同类型数据的指针,因为这样原本的数据类型在 TList 对象中丢失了,当取出元素指针时您无法获知这个指针指向的究竟是何种类型的数据。
而 TInterfaceList 的元素都是接口,无法存放接口以外的类型。
TStringList 既可以作为一个纯粹存放字符串元素的 List,也可以作为一个通过字符串来存取对象的 Map,不过它不是通过 Hash 方式查找的,所以效率不高,操作方式也不同于一般的 Map 类型,不是很方便。而且,用它也无法存取字符串和对象以外的类型。
Delphi 2009 以后引入的泛型容器基本上解决了上述问题,但是对于低版本的 Delphi 和 FreePascal 来说,却无法使用。
为了解决这个问题,Hprose 提供了一套可变类型的 List。
这套可变类型的 List 是基于接口来实现的,因此具有引用计数的自动内存管理功能。
+----------------+
| IReadWriteSync |
+----------------+
|
v
+----------------+ +-------------------+ +-----------------+
| IImmutableList | | TInterfacedObject | | IListEnumerator |
+----------------+ +-------------------+ +-----------------+
| |
v v
+-------+ +---------------+ +----------------------+
| IList |-------->| TAbstractList |<---------| IInvokeableVarObject |
+-------+ +---------------+ +----------------------+
| |
| | +---------------------------+
+---------+---------------------+------------>| ICaseInsensitiveArrayList |
| | | +---------------------------+
| | | |
| v v v
| +------------+ +------------+ +---------------------------+
+--| IArrayList |------->| TArrayList |------>| TCaseInsensitiveArrayList |
+------------+ +------------+ +---------------------------+
| |
| | +----------------------------+
+---------+---------------------+----------->| ICaseInsensitiveHashedList |
| | | +----------------------------+
| | | |
| v v v
| +-------------+ +-------------+ +----------------------------+
+--| IHashedList |------>| THashedList |---->| TCaseInsensitiveHashedList |
+-------------+ +-------------+ +----------------------------+
IImmutableList
接口中提供了一些对列表只读操作的一些方法和属性,其定义如下:
IImmutableList = interface(IReadWriteSync)
['{1D116D24-E964-E019-FC27-AFE4BF2A181D}']
function Get(Index: Integer): Variant;
function GetCapacity: Integer;
function GetCount: Integer;
function Contains(const Value: Variant): Boolean;
function GetEnumerator: IListEnumerator;
function IndexOf(const Value: Variant): Integer; overload;
function IndexOf(const Value: Variant; StartIndex: Integer): Integer; overload;
function IndexOf(const Value: Variant; StartIndex, ACount: Integer): Integer; overload;
function LastIndexOf(const Value: Variant): Integer; overload;
function LastIndexOf(const Value: Variant; StartIndex: Integer): Integer; overload;
function LastIndexOf(const Value: Variant; StartIndex, ACount: Integer): Integer; overload;
function Join(const Glue: string = ',';
const LeftPad: string = '';
const RightPad: string = ''): string;
procedure Lock;
procedure Unlock;
function ToArray: TVariants; overload;
function ToArray(VarType: TVarType): Variant; overload;
function First: Variant;
function Last: Variant;
property Item[Index: Integer]: Variant read Get; default;
property Capacity: Integer read GetCapacity;
property Count: Integer read GetCount;
end;
它是 IList
的父接口。
你可能会奇怪 IImmutableList
作为一个不变列表接口,其实是不需要锁的,为何要继承 IReadWriteSync
接口呢?
原因是 Delphi 的接口本身只支持单继承。如果不在 IImmutableList
中继承 IReadWriteSync
接口的话,就没有办法在 IList
接口中同时继承 IImmutableList
和 IReadWriteSync
这两个独立的接口。
IImmutableList
中还提供了枚举器操作:
function GetEnumerator: IListEnumerator;
该操作返回一个 IListEnumerator
接口对象。IListEnumerator
接口定义如下:
IListEnumerator = interface
['{767477EC-A143-4DC6-9962-A6837A7AEC01}']
function GetCurrent: Variant;
function MoveNext: Boolean;
procedure Reset;
property Current: Variant read GetCurrent;
end;
有了该操作之后,在 Delphi 2005 以上或 FreePascal 2.5.1 以上的版本中,就可以通过 for...in
语句来操作 Hprose 中所有列表接口的对象了。
该接口继承自 IImmutableList
接口。
它在 IImmutableList
接口的基础上,增加了添加、删除、修改、排序、翻转等操作。它的定义如下:
IList = interface(IImmutableList)
['{DE925411-42B8-4DB3-A00C-B585C087EC4C}']
procedure Put(Index: Integer; const Value: Variant);
procedure SetCapacity(NewCapacity: Integer);
procedure SetCount(NewCount: Integer);
function Add(const Value: Variant): Integer;
procedure AddAll(const AList: IImmutableList); overload;
procedure AddAll(const Container: Variant); overload;
procedure AddAll(const ConstArray: array of const); overload;
procedure Assign(const Source: IImmutableList);
procedure Clear;
function Compare(const Value1, Value2: Variant): Integer;
function Delete(Index: Integer): Variant;
procedure DeleteRange(Index, ACount: Integer);
procedure Exchange(Index1, Index2: Integer);
procedure Insert(Index: Integer; const Value: Variant);
procedure InsertRange(Index: Integer; const AList: IImmutableList); overload;
procedure InsertRange(Index: Integer; const Container: Variant); overload;
procedure InsertRange(Index: Integer; const ConstArray: array of const); overload;
procedure InitLock;
procedure InitReadWriteLock;
procedure Move(CurIndex, NewIndex: Integer);
function Remove(const Value: Variant): Integer; overload;
function Remove(const Value: Variant; Direction: TDirection): Integer; overload;
procedure Pack;
procedure Reverse;
procedure Sort; overload;
procedure Sort(CompareProc: TListCompareMethod); overload;
procedure Shuffle;
procedure TrimExcess;
property Item[Index: Integer]: Variant read Get write Put; default;
property Capacity: Integer read GetCapacity write SetCapacity;
property Count: Integer read GetCount write SetCount;
end;
后面我们会在介绍具体实现类的时候来详细讲解这些操作。
该类是所有可变类型 List 的基类,它实现了 IList
接口上的枚举器,复制和同步等操作。它继承自 TInterfacedObject
,因此它也继承了接口生存周期管理。它是一个抽象类,您不应该对它进行实例化。
如果您打算实现自己的可变类型 List,那么您应该直接或间接的继承自它。因为在 Hprose 序列化和反序列化时,判断一个类是否是可变类型的 List 是通过判断 TAbstractList
是否是这个类的祖先类的方式完成的。
该类上还实现了一个类方法 Split
。但因为 TAbstractList
是抽象类,所以您只能在它的可实例化子类上调用它。该方法的具体用法,我们在下面介绍 TArrayList
类时来详细介绍。
IArrayList = interface(IList)
['{0D12803C-6B0B-476B-A9E3-C219BF651BD1}']
end;
该接口继承自 IList
接口,并且没有添加任何操作。你可能会觉得这样定义比较奇怪,因为在平时使用时,该接口似乎是没有什么用处的。
这样做的原因是为了在反序列化列表类型数据时,可以通过指定该具体的接口,来反序列化为具体的实现类对象。
IArrayList
接口对应 TArrayList
实现。
IHashedList
接口对应 THashedList
实现。
ICaseInsensitiveArrayList
接口对应 TCaseInsensitiveArrayList
实现。
ICaseInsensitiveHashedList
接口对应 TCaseInsensitiveHashedList
实现。
如果反序列化时,指定的类型是 IList
或者 IImmutableList
接口类型,那么反序列化时,也会反序列化为 TArrayList
实现的具体对象。
关于序列化和反序列化的内容,我们会在后面具体的章节中在详细介绍。
该类直接继承自 TAbstractList
,它是最常用的 List。它内部是通过动态数组实现的。
TArrayList
有多个构造方法,其中有一部分继承自 TAbstractList
:
constructor Create(ACapacity: Integer = 4; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; override;
constructor Create(Sync: Boolean; ReadWriteSync: Boolean = False); overload; virtual;
constructor Create(const AList: IImmutableList; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; virtual;
constructor Create(const Container: Variant; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; virtual;
constructor Create(const ConstArray: array of const; Sync: Boolean = True; ReadWriteSync: Boolean = False); overload; virtual;
ACapacity
参数是初始化容量,如果您事先确知要放入的元素个数,那么将容量设置为与元素个数相同或多于元素个数,可以在后面放入元素时,避免内存重新分配。该参数默认值为 4。
初始化容量不代表这个 List 只能放多少个元素,也不代表这个 List 有多少个元素。它只代表在不重新分配内存的情况下,能放入的元素个数。当元素个数增长到超过这个容量时,这个容量会自动扩大,您不需要做任何特殊操作。
Sync
表示是否默认创建带有线程同步锁的 List 对象。默认为 True
,如果你不需要在程序对该 List 做 Lock
、UnLock
操作,那么你可以将该参数设置为 False
。即使创建了不带锁的 List 对象,后面还可以通过调用该对象的 InitLock
方法来初始化锁,之后 Lock
和 UnLock
方法也可以正常使用。
ReadWriteSync
是表示是否默认创建带多读单写锁的 List 对象。,如果需要该功能,在通过构造函数创建对象时,只需要将 ReadWriteSync
参数设置为 True
即可,该参数默认为 False
。之后可以通过 BeginRead
、EndRead
、BeginWrite
、EndWrite
这四个方法来操作读写锁。也可以在创建不带锁的对象后,单独调用 InitReadWriteLock
来初始化多读单写锁。
创建 List 对象时,可以设置初始值,初始值可以是一个 IImmutableList
对象(即所有的 Hprose 中定义的 List 对象),或者一个 Variant
类型的变量,该变量中保存的值需要是一个 List 对象或者是一个动态数组,如果是其它类型的 Variant 类型的变量,将会被忽略。初始值还支持 array of const
类型的参数。
通常创建 TArrayList 对象之后,不应该直接保存为 TArrayList 类型的对象,这样需要自己手动管理内存,更方便的方法是把返回的对象保存为一个 IList 的接口变量中。Hprose 还提供了一个简单的创建带有初始值的函数:
function ArrayList(const Elements: array of const): IArrayList;
例如:
procedure CreateDemo;
var
L: IArrayList;
I: Integer;
begin
L := ArrayList([1, 2, 3]);
for I := 0 to L.Count - 1 do
Writeln(L[I]);
end;
Hprose 最新的版本还支持将一个 IList
保存到 Variant
变量中,并直接在这个变量上操作 IList
上的属性和方法。不过需要注意的是,不能直接在 Varaint
变量上像操作数组那样直接通过下标来存取元素,例如:
procedure CreateDemo2;
var
L: Variant;
I: Integer;
begin
L := ArrayList([1, 2, 3]);
for I := 0 to L.Count - 1 do
Writeln(L.Get(I));
end;
这里获取元素需要用 Get
方法。
您可以通过 Add
方法一次添加一个元素,或通过 AddAll
方法一次添加多个元素。看下面的例子:
procedure AddDemo;
var
List, List2: IList;
I: Integer;
begin
List := TArrayList.Create;
List.Add(1);
List.Add('abc');
List.Add(3.14);
List.Add(True);
List2 := TArrayList.Create;
List2.AddAll(List);
List2.AddAll(['a','b','c']);
for I := 0 to List2.Count - 1 do Writeln(List2[I]);
List2.Add(List);
List := VarToList(List2.Last());
for I := 0 to List.Count - 1 do Writeln(List[I]);
end;
通过这个例子,您会发现可以添加任意类型(可转换为 Variant
类型的类型)的数据到 IList
对象中,甚至 IList
对象本身也可以作为元素添加到 IList
对象中,并且可通过 AddAll
将 IList
对象或者动态数组中的元素批量添加到 IList
对象中。
上面例子的运行结果为:
1
abc
3.14
True
a
b
c
1
abc
3.14
True
上面代码中我们使用 List2.Last
方法来获取列表的最后一个元素,它相当于 List2[List2.Count - 1]
,但是写起来更简单。另外,与之对应的,还有一个 First
方法可以用来获取第一个元素。
使用 Insert
方法可以在列表的任何位置插入元素,例如:
procedure InsertDemo;
var
List: IList;
I: Integer;
begin
List := ArrayList([1, 'abc', 3.14, True]);
List.Insert(0, 'top');
List.Insert(3, 'middle');
List.Insert(6, 'bottom');
for I := 0 to List.Count - 1 do Writeln(List[I]);
end;
运行结果如下:
top
1
abc
middle
3.14
True
bottom
另外,你还可以通过 InsertRange
方法来一次插入一组数据,例如:
procedure InsertRangeDemo;
var
List: IList;
I: Integer;
begin
List := ArrayList([1, 'abc', 3.14, True]);
List.InsertRange(2, [1, 2, 3]);
for I := 0 to List.Count - 1 do Writeln(List[I]);
end;
运行结果如下:
1
abc
1
2
3
3.14
True
通过 Move
方法可以将一个元素从一个位置移动到另一个位置,例如:
procedure MoveDemo;
var
List: IList;
I: Integer;
begin
List := ArrayList([1, 'abc', 3.14, True]);
List.Move(2, 0);
for I := 0 to List.Count - 1 do Writeln(List[I]);
end;
运行结果如下:
3.14
1
abc
True
通过 Exchange
方法可以将一个元素从一个位置移动到另一个位置,例如:
procedure ExchangeDemo;
var
List: IList;
I: Integer;
begin
List := ArrayList([1, 'abc', 3.14, True]);
List.Exchange(2, 0);
for I := 0 to List.Count - 1 do Writeln(List[I]);
end;
运行结果如下:
3.14
abc
1
True
通过 Contains
, IndexOf
或 LastIndexOf
方法我们可以确认一个元素是否在 IList
对象,例如:
procedure FindDemo;
var
List: IList;
begin
List := ArrayList([1, 'abc', 3.14, True, 'abc']);
Writeln(List.Contains('hello'));
Writeln(List.Contains('abc'));
Writeln(List.IndexOf(3.14));
Writeln(List.IndexOf(False));
Writeln(List.LastIndexOf('abc'));
Writeln(List.LastIndexOf(1));
end;
运行结果如下:
FALSE
TRUE
2
-1
4
0
通过上面的例子我们还可以看出 Contains
, IndexOf
和 LastIndexOf
方法的不同之处。Contains
方法只返回存在与否。IndexOf
方法会返回具体位置,如果不存在,则返回 -1
。LastIndexOf
方法也是返回具体的位置,但是它是从后往前查找。
另外,IndexOf
和 LastIndexOf
方法还可以指定开始查找位置 StartIndex
,以及从开始查找位置起要检查的元素个数 ACount
。
这三个方法在 TArrayList
中的时间复杂度都是 O(n),这与后面将要介绍的 THashedList
和 TCaseInsensitiveHashedList
是不同的,后面我们在介绍它们时再举例说明。
通过 Delete
或 Remove
都可以删除元素。
Delete
是根据索引删除并返回删除的元素,例如:
procedure DeleteDemo;
var
List: IList;
begin
List := ArrayList([1, 'abc', 3.14, True]);
Writeln(List.Delete(2));
Writeln(List.Delete(2));
end;
运行结果为:
3.14
True
Remove
是根据元素删除并返回删除的索引,并且可以设置查找元素的方向,例如:
procedure RemoveDemo;
var
List: IList;
begin
List := ArrayList([1, 'abc', 3.14, True, 'abc']);
Writeln(List.Remove('abc', FromEnd));
Writeln(List.Remove('abc', FromBeginning));
Writeln(List.Remove(True));
end;
运行结果为:
4
1
2
通过上面的结果,我们还会发现,元素删除后,后面的元素会自动往前移动。
与插入元素类似,删除元素也提供了一个 DeleteRange
方法用于一次删除一组元素。Index
参数是开始删除的位置,ACount
是要删除的元素个数。它比你使用 Delete
一个一个删除要快得多。
通过 Clear
方法可以清空所有元素,如果要删除 IList
对象中的所有元素,使用 Clear
比使用 Delete
或 DeleteRange
都快很多。
例如:
procedure ClearDemo;
var
List: IList;
begin
List := ArrayList([1, 'abc', 3.14, True]);
List.Clear;
Writeln(List.Count);
end;
结果为:
0
通过 Assign
方法可以将本对象变为指定对象一个副本。该方法非常简单,这里不再举例说明。
当您在多个线程中操作同一个 IList
对象时,为了保证数据的线程安全性,需要对这个对象进行同步操作。通过 Lock
、UnLock
方法可以非常方便的实现对 IList
对象自身的线程同步。只需要在操作该对象前,调用该对象的 Lock
方法,所有操作完成后,再调用 Unlock
方法即可。
如果你在对共享的 IList
对象进行读写操作时,读操作远远多于写操作,可以使用多读单写锁来提高性能。这部分在 创建 TArrayList 对象一节已经介绍了,这里就不在重复了。
通过 ToArray
方法,可以将 IList
对象直接转换为动态数组。例如:
procedure ToArrayDemo;
var
List: IList;
A: array of Integer;
I: Integer;
begin
List := ArrayList([1, 3, 4, 5, 9]);
A := List.ToArray(varInteger);
for I := 0 to Length(A) - 1 do Writeln(A[I]);
end;
运行结果如下:
1
3
5
7
9
如果您使用 Delphi 2005 或更高版本,或者 FreePascal 2.5.1 或更高版本,您可以通过 for...in
语句来枚举 IList
接口的元素:
procedure ForInDemo;
var
List: IList;
V: Variant;
begin
List := ArrayList([1, 'abc', 3.14, True]);
for V in List do Writeln(V);
end;
通过 TArrayList
上的类方法 Split
,可以直接将一个字符串分割为 TArrayList
对象。Split
有 7 个参数,这 7 个参数的含义分别是:
-
Str
表示待分割的字符串。 -
Separator
表示分割符,默认值是逗号(,
),它可以是单个字符,也可以是字符串。如果该分割符为空字符串,则返回只包含一个完整Str
元素的TArrayList
对象。 -
Limit
表示最多分割为几个元素,默认值是0
,表示没有限制。 -
TrimItem
表示是否去掉分割后的字符串的首尾空格,默认为False
。 -
SkipEmptyItem
表示是否忽略空串元素,默认为False
。 -
Sync
跟TArrayList
构造方法的Sync
参数意义相同。 -
ReadWriteSync
跟TArrayList
构造方法的ReadWriteSync
参数意义相同。
例如:
procedure PrintList(List: IList);
var
I: Integer;
begin
for I := 0 to List.Count - 1 do Writeln(List[I]);
Writeln('--------');
end;
procedure SplitDemo;
var
S: String;
begin
S := 'Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday,';
PrintList(TArrayList.Split(S));
PrintList(TArrayList.Split(S, ', '));
PrintList(TArrayList.Split(S, ',', 4));
PrintList(TArrayList.Split(S, ',', 0, true));
PrintList(TArrayList.Split(S, ',', 0, true, true));
end;
该程序的运行结果为:
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
--------
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday,
--------
Monday
Tuesday
Wednesday
Thursday, Friday, Saturday, Sunday,
--------
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
--------
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
--------
天下大势,分久必合,合久必分。前面讲了分割字符串,那肯定就少不了合并字符串。
如果 IList
对象中所有的元素都是字符串,或者都可以转换为字符串类型,那么就可以通过 Join
方法,将这些元素以指定的方式连接成一个字符串。Join 方法有 3 个参数:
-
Glue
参数是用于合并两个字符串之间字符串,默认是逗号(,
)。 -
LeftPad
参数是补在合并之后的字符串开头的字符串,默认是空字符串。 -
RightPad
参数是补在合并之后的字符串结尾的字符串,默认是空字符串。
例如:
procedure JoinDemo;
var
S: String;
List: IList;
begin
S := 'Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday';
List := TArrayList.Split(S);
Writeln(List.Join);
Writeln(List.Join('; '));
Writeln(List.Join('", "', '["', '"]'));
end;
运行结果为:
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
Monday; Tuesday; Wednesday; Thursday; Friday; Saturday; Sunday
["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
可以通过 Item
属性来存取 IList
中的元素,因为 Item
为默认属性,所以 Item
可省略。也就是说,可以用存取数组元素的方法来存取 IList
的元素。
但如果是将 IList
对象保存在了 Variant
变量中,则只能使用 Get
和 Put
方法来存取元素。
当使用 Item
属性或者 Put
方法来放入元素时,如果下标超过原最大下标,只要没有超过 MaxListSize
,列表会自动扩容。但如果下标小于 0 或者超过 MaxListSize
则会抛出异常。
当使用 Item
属性或者 Get
方法来获取元素时,如果下标越界会返回 Unassigned
这样一个 Variant
值,不会抛出异常。
例子暂且不举,下面这个方法中你会马上看到例子。
IList
提供了一个 Pack
方法用于把 IList
对象中所有值不是 Unassigned
的元素聚在一起,同时把 Count
的属性值改变,这样,所有值为 Unassigned
的元素就会被删除。但是该操作并不会减少 Capacity
,如果你想把没用的空间都释放掉的话,可以再调用 TrimExcess
方法,它会把 Capacity
设置成 Count
。
procedure PackDemo;
var
L: IList;
N: Integer;
begin
L := ArrayList([1, 'abc', 3.14, True]);
L[10] := 'test';
Writeln(L.Count);
Writeln(L[10]);
N := L.Capacity;
L.Pack;
Writeln(L.Count);
Writeln(L[4]);
Writeln(L.Capacity = N);
L.TrimExcess;
Writeln(L.Capacity = N);
Writeln(L.Count = L.Capacity);
end;
运行结果为:
11
test
5
test
True
False
True
如果你希望把列表中的元素位置翻转,可以使用 Reverse
方法,例如:
procedure ReverseDemo;
var
List: IList;
begin
List := ArrayList([1, 'abc', 3.14, True]);
List.Reverse;
Writeln(List.Join);
end;
运行结果为:
True,3.14,abc,1
如果列表中的元素是可排序的,那么可以通过 Sort
方法进行排序。所谓可排序是指任意两个元素之间都是可以比较大小的。默认是按照从小到大的顺序排列的,你也可以自己定制排序比较方法。
procedure SortDemo;
var
List: IList;
begin
List := ArrayList([3, 5, 1, 2, 4, 9, 7, 6, 8]);
List.Sort;
Writeln(List.Join);
end;
运行结果为:
1,2,3,4,5,6,7,8,9
除了可以把 IList
中的元素进行排序,还可以把元素的顺序打乱,使用 Shuffle
方法可以将 IList
中的元素顺序随机打乱,洗牌算法跟排序算法不同,洗牌算法不要求元素之间是可比较的。例如:
procedure ShuffleDemo;
var
List: IList;
begin
List := ArrayList([1, 'abc', 3.14, True]);
List.Shuffle;
Writeln(List.Join);
end;
该程序运行结果几乎每次都不相同。
该类继承自 TArrayList
。它内部增加了一个 HashBucket
来管理每个元素的 Hash
值与索引之间的关系。这使得在该类的对象上执行Contains
、IndexOf
、LastIndexOf
、Remove
操作的时间复杂度为 O(1),而 TArrayList
上执行这些个操作的时间复杂度为 O(n)。
虽然在包含大量元素时,使用 THashedList
进行反查索引操作要比 TArrayList
快的多,但是 THashedList
也会占用更多的空间,并且在进行其它操作时,比 TArrayList
要稍慢一些,因此,只有当你需要快速反查索引时,才有必要使用 THashedList
。
它的操作和效果与 TArrayList
完全相同。这里就不在单独举例了。
该类继承自 TArrayList
,因此在该类的对象上执行 Contains
、 IndexOf
、 LastIndexOf
、 Remove
操作的时间复杂度为 O(n)。但与 TArrayList
不同的是,如果在执行 Contains
、 IndexOf
、 LastIndexOf
、 Remove
操作时,查找的是字符串元素,该类是不区分大小写的。
它的其它操作的效果与 TArrayList
完全相同。这里也不在单独举例了。
该类继承自 THashedList
,因此在该类的对象上执行 Contains
、 IndexOf
、 LastIndexOf
、 Remove
操作的时间复杂度为 O(1)。但与 THashedList
不同的是,如果在执行 Contains
、 IndexOf
、 LastIndexOf
、 Remove
操作时,查找的是字符串元素,该类是不区分大小写的。
它的其它操作的效果与 THashedList
完全相同。这里也不在单独举例了。