我应该总是使用"TList<String>"而不是"String数组"吗?

7

我一直在迁移一个使用Delphi 7开发的项目,并且每次查看代码时,之前的开发人员都会创建类似以下代码的集合:

ArrayOfString : array of string;
ArrayOfInteger: array of integer;
ArrayOfStringArrays : array of ArrayOfString;

这种代码在许多地方都会被重复使用,而且每个地方的 "SetLength" 也会被多次设置,我想知道现在我们是否可以将所有这些 Array of Something 更改为 TList<Something>,因为我们现在使用的是 Delphi XE4。
这样做有什么优点,在资源、速度或其他方面能够支持我做出这个决定吗?
PS:我来自 JAVA,对我来说 Delphi 感觉很黑暗,充满了恐惧。

3
那么欢迎来到黑暗和恐惧的世界! :) - TLama
3个回答

10

将动态数组视为比TStringListTList<T>更低级的构造。动态数组通过指针直接访问元素,语言会隐藏指针,但这基本上就是动态数组的全部。但是你需要处理任何重新分配以及如果你想要插入或删除项目,则必须编写代码并处理详细信息。

较高级别的集合类TStringListTList<T>是建立在动态数组之上的,内部存储内容的方式也是如此。但是集合类为你包装了一切。它们提供了像插入和删除等更高级别的操作作为方法。实质上,这些集合类比原始的动态数组提供了更多方便。

举个例子,考虑插入一个项目。对于动态数组,你需要执行以下操作:

  1. 调整数组大小。
  2. 将插入点后面的项目从位置i移动到i+1。
  3. 赋值已插入的项目。

如果你需要多次编写此代码,那么可能做错了。

对于高级别集合,你只需要编写:

List.Insert(Index, Value);

让类来处理细节。

请注意,出于历史原因,并且字符串特别重要,开发人员倾向于使用专门的、定制的 TStringList 类,而不是 TList<string>。再次,这个专用类提供了超越 TList<string> 的功能,因为它专门针对字符串并可以提供针对字符串的专门功能。同样,这个专用类也提供了方便性。

动态数组非常有用的一个地方是当你不想承担生命周期管理的模板代码时。因此,对于没有 ARC 的类的桌面编译器,需要显式销毁 TList<T> 实例。但是动态数组的生命周期由 ARC 管理。如果你一次性合成数组,然后不调整大小,那么生命周期管理问题可以使数组更加方便易用。

作为经验法则,请优先考虑使用高级集合类。它们应该是您的默认选择。有时候动态数组是正确的选择,但这往往是针对更专业的场景。


我不想为此提出一个新问题,但是有一个项目,其中所有内容都使用动态数组完成,我想知道是否将其替换为TList<T>是一个好主意,还是保持原样...你能给我建议吗? - Diego Rueda
无法从这里判断。 - David Heffernan

6
首先,在Delphi的更新版本中,有一个TArray类型可以用来替换所有那些旧的array of whatever声明。使用它可以解决语言中一些长期存在的问题,这些问题来自于经典Pascal,并且帮助您避免在以后遇到混乱的错误。
但是要注意,TStringList不是一个字符串数组;它是一个容器对象,用于保存字符串列表。它有几个专门处理字符串列表的方法,使其非常灵活:它基本上可以被用作一个字符串列表,一组字符串(通过.Sorted属性),一个字符串/对象映射(通过.Objects属性)和一个字符串/字符串映射(通过.Names和.Values属性)。
如果您发现正在频繁调用SetLength来定义数组大小,特别是如果它类似以下内容,那么您应该将其转换为一个列表类:
for i := 0 to Count - 1 do
begin
   SetLength(myArray, length(myArray) + 1);
   myArray[high(myArray)] := values[i];
end;

使用通用的 TList<T> 或者 TStringList 时,可以这样实现:
for i := 0 to Count - 1 do
begin
   myList.Add(values[i]);
end;

这种方法更易于阅读,而且在 Count 值较大时性能显著提高(特别是列表类能够内部跟踪其大小并保持内存重新分配。因此,列表类在大多数情况下都是双赢的局面。但是要注意一点:如果你来自 Java 背景,请记住动态数组是由编译器管理的类型,而列表类是需要在代码中释放的对象。

“...并且性能更高” 那么列表确实比数组处理速度更快吗? - Diego Rueda
@sandiego 如果你注意最小化重新分配,动态数组将提供最佳性能。然而,我没有看到任何证据表明perf是你的主要问题。它是吗? - David Heffernan
2
@Mason 在计算机科学中,Delphi 所称的列表实际上是一个数组。它是由同类型的项目组成的集合,通过一系列连续的整数进行索引。在计算机科学中,列表意味着链表的某种形式。Emba 选择的术语最好说是不幸的。Delphi 列表具有数组的性能特征。 - David Heffernan
5
"Counter Strike术语"的意思是什么? - EProgrammerNotFound
3
@PageNotFound 计算机科学 - David Heffernan
显示剩余2条评论

0

除非你有特定需要使用字符串数组,否则我强烈建议使用TStringList。个人而言,我不会使用TList。TStringList提供了我所需的一切,只有在极少数情况下才需要使用字符串数组。

TStringList可以通过myStringList.Items[i]或甚至只是myStringList[i]来像数组一样访问。

你不需要改变长度或保持计数,只需使用myStringList.Add(aString)和myStringList.Count(别忘了循环时要减1)。

此外,它还具有添加、删除、重新排序、排序等所有列表操作所需的例程。

它也可以作为参数传递,无需像数组类型那样进行其他声明。

希望这能帮到你。


如果您想要一个非字符串的物品集合呢? - David Heffernan
如果你想要一个对象集合,那么TObjectList<T>就是这项工作的工具。它拥有所有TStringList的优点,但适用于对象。如果你真的非常需要使用数组,那也可以,但如果你需要一个集合,那么对象使用TObjectList<T>,字符串使用TStringList。 - EchelonKnight
2
只有在需要列表拥有对象时才使用 TObjectList<T>,否则 TList<T> 更好。但是对于整数集合,如问题所示,怎么办? - David Heffernan
我在原问题中没有看到整数数组..哎呀。我已经为Integers制作了一个自定义的List类,可以使用数组或TList后代来提供功能。使用TList后代提供了我上面提到的所有TList的优点。TObjectList.Create(False) 确保对象列表不拥有对象以进行销毁。我不明白为什么TList更好。TObjectList和TObjectList<T>都是从TList和TList<T>派生而来,但提供了更多的功能。 - EchelonKnight
忘掉非泛型版本吧,没人会用它们来写新代码。TObject<T> 提供的额外功能是拥有对象。如果你不想要,就不要使用它。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接