在 Delphi 中从 TList 开头删除大块项目的有效方法是什么
从 TList 中删除 (0) 的成本很高,因为所有后续项都需要向下移动。如果我需要从更大列表的开头删除大量项目,最快的方法是什么?
Delete (0) from a TList is expensive because all the subsequent items need to be moved down. If I need to delete a large number of items from the start of an even larger list what's the fastest way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
从
TList
开头删除大量元素的成本很高。尽管类名具有欺骗性,但TList
实际上是一个数组。在TList
中,没有删除范围的功能 - 每个项目必须单独删除,然后列表的其余部分向下移动。对于较大的范围,这将引发大量的重新分配和完整的列表移动。如果您有更现代的 Delphi,您可以使用通用列表类
TList
并利用DeleteRange
方法。该文档包含以下重要说明:在 Delphi 2006 中,您可以编写具有同等性能特征的代码,如下所示:
Deleting a large range of elements from the beginning of a
TList
is expensive. Although the class name flatters to deceive, aTList
is in fact an array. InTList
there is no facility to delete a range–each item must be deleted individually and then the rest of the list is moved down. For a large range that's going to provoke an awful lot of reallocations and full list moves.If you had a more modern Delphi you could use the generic list class
TList<T>
and avail yourself of theDeleteRange
method. The documentation includes this vital note:In Delphi 2006 you can write something with equivalent performance characteristics like this:
正如 Marcelo 已经说过的,您可以复制整个块,但您可以使用
TList.List
作为参数,通过一次调用 Move() 来移动整个块,而不是逐项执行该操作。请注意,
TList.List
是一个PPointerList
(^TPointerList; TPointerList = array[0..MaxListSize - 1] of Pointer;
)较旧的 Delphi 版本并在 Delphi XE2 中成为TPointerList
(TPointerList = array of Pointer;
),因此您应该使用正确的间接寻址:
As Marcelo already said, you could copy down the whole block, but instead of doing that item by item, you could move the entire with one call to Move(), using
TList.List
as an argument.Do note that
TList.List
was aPPointerList
(^TPointerList; TPointerList = array[0..MaxListSize - 1] of Pointer;
) in older Delphi versions and became aTPointerList
(TPointerList = array of Pointer;
) in Delphi XE2, so you should use the correct indirection:and
由于 TList 是一个指针数组,因此在 XE2 中执行此操作的方法如下。
Delphi 2006 上的实现类似,但我无法编写代码,因为我没有 2006。
只要指针指向的所有内容都在其他地方进行内存管理,就不会发生泄漏。
这是证明:
该证明存在严重内存泄漏,但我们创建对象只是为了显示值。
Here's how you do it in XE2, since TList is an array of pointers.
The implementation will be similar on Delphi 2006, but I can't write the code since I don't have 2006.
As long as all of the things the pointers point to are memory managed elsewhere, there's no leak.
Here's the proof:
The proof has major memory leaks, but we created the objects just to show the values.
如果顺序很重要,并且您有 N 个项目需要在前面删除:
我还没有很好地思考这段代码,因此您需要检查是否存在差一错误等。
如果顺序不重要,您可以简单地将最后 N 项与前 N 项交换,然后按上述方式删除最后 N 项。
If order matters, and you have N items to remove at the front:
I haven't thought this code through very well, so you'll need to check for off-by-one errors and the like.
If order doesn't matter, you can simply exchange the last N items with the first N items, and the delete the last N items as above.
这里有一个想法:如果您知道列表中的所有项目都已分配,则可以将要删除的项目置零,然后只需调用 TList.Pack(),它会找出空位在哪里,并尽可能有效地将其他所有项目移到一边。不过,这需要扫描所有项目,因此它可能不是您想要的,但它也不使用删除(从而阻止 Nitofy 调用)。 Pack 的实现在 D2006 和 XE2 之间没有发生任何变化,因此您可以依赖它的效率。
请注意,要删除要删除的项目,您可以使用
List[aIndex] := nil
但这仍会强制调用 Notify(),因此List.List[aIndex] : = nil 可能会更快。
Here's a thought : If you know all items in your List are assigned, you could nil the ones you want to remove and just call TList.Pack(), which figures out where the empty spots are and moves everything else aside as efficiently as possible. This requires a scan through all items though, so it might not be what you want, but it doesn't use Delete (and thus prevents Nitofy calls) also. The implementation of Pack hasn't changed a bit between D2006 and XE2, so you can depend on it's efficiency.
Note, that to nil the items you want removed, you could use
List[aIndex] := nil
but that would still impose a Notify() call, soList.List[aIndex] := nil
might be faster for that.首先,使用 BeginUpdate 和 EndUpdate 来防止通过删除每个项目来更新 TList 的 UI。
第二:尝试先删除列表中最下面的项目。换句话说,从列表中从下到上删除项目对其他项目的效率较低。
First of all, use BeginUpdate and EndUpdate to prevent updating the UI of the TList by deleting each item.
second: try to delete the items at the most lower one in the list first. In other word, deleting items from down to top the list takes less efficient on other items.