【问题标题】:What data structure is best suited for VirtualStringTree?哪种数据结构最适合 VirtualStringTree?
【发布时间】:2011-01-16 20:50:29
【问题描述】:

我想曾经使用过 Delphi 的 VirtualStringTree 的每个人都会同意它是一个很棒的控件。这是一个“虚拟”控件(您的数据必须保存在其他地方)所以我在想哪种数据结构最适合这样的任务? IMO 认为数据结构必须支持层次结构,它必须快速且易于扩展。最简单的实现是使用记录,这就是大多数可以找到的文档所建议的。但是,如果您需要进行一些快速查找、计算总数等怎么办?与 VirtualStringTree 一起使用的数据结构是什么?

EDIT1:我使用的是 Delphi 2010。

好的,我将尝试提供有关我的要求的更多详细信息。 数据大小可以变化很大,从 1 到数千个项目。每个项目可以包含多个字符串、整数值。我需要随机访问,我的数据可以在应用程序生命周期内多次更改。良好的性能是非常可取的。我还需要数据保存和重新加载。

EDIT2: 得到 1 个答案,所以我会尝试评论我的意见。谢谢,多林你的回答,但是 我不认为你的结构很方便。 1)它不处理层次结构。 2) 每个节点都有单独的 TStringList 或 TList 不是很有效的 IMO。通过这种实现,我只能查找当前节点的数据,但不能有效地在整个树中搜索。

我觉得这个数据结构一定像一棵树。它必须具有能够添加子节点的节点。然后我可以在 OnInitNode 事件中获取节点的数据,检查我的节点是否有一些子节点,如果有则设置 ivsHasChildren 标志,然后在 OnInitChildren 事件中设置正确的子节点计数。稍后在 OnGetText 事件中,我可以从节点结构中获取所需的数据,并根据 Column 索引将其设置为 CellText。我的想法是拥有一个单独的数据结构并使用它进行所有需要的操作,而无需使用 VirtualStringTree。希望有人明白我的意思:)。

EDIT3:我发现了非常有趣的 JclTrees 单元,乍一看可以用来实现我正在寻找的东西。它属于JCL 库。缺乏像样的文档使得很难快速调查它的功能。当我有更多时间时,我可能会更深入地研究它。

【问题讨论】:

  • @Linas 您能否告诉我们更多有关您希望存储的数据的信息。它有多大?它在您的流程生命周期中如何变化?你需要随机访问吗?性能限制是什么?目前我觉得您的问题过于笼统,无法提供有意义的建议。
  • @Linas 随机访问意味着你需要一个数组。
  • @David a TList 恕我直言,但数组也可以。
  • @dorin TList 是一个数组
  • @David 是的,它是一个指针数组,但使用起来更容易:-) 这是我的观点。

标签: delphi data-structures virtualtreeview


【解决方案1】:

好的,因为给出的答案并没有解决我的问题,所以我编写了自己的树数据结构,它模仿 TVirtualStringTree 并处理我在问题中提到的所有问题。现在我可以选择只使用我的数据结构,其中的所有更改都会自动更新 VirtualStringTree。我想我稍后会在某处上传源代码并在此处发布链接。谢谢大家的回答。

编辑:我已将源代码上传到 Google 代码:svTrees。有一个小演示展示了它的工作原理。

【讨论】:

  • 很好,您已经解决了,但我想说您没有得到满意答案的一个重要原因是您的问题有些模糊。
  • @David 也许你是对的,但解决方案并不容易实施。也许是因为这种复杂性,我很难描述我的问题。
  • 可能只是想在这里解释它帮助您更好地理解问题!它通常是这样工作的。
【解决方案2】:

你还没有指定你的 Delphi 版本,所以:

我建议使用记录(我不确定他们在哪个版本的 Delphi 中添加了记录方法,我从 D7 移到了 D2010)所以你可以有类似的东西:

type
  TMyRecordWithMethods = record
    function GetMeAResult: Integer;
    procedure DoSomething(const AInParam: Integer; var AOutParam: Integer);
  end;

如果您的 Delphi 版本不支持带有方法的记录,而您确实需要用于节点的方法,那么您将不得不使用对象来完成此操作,还可以查看泛型。

由于您只需要保存几千件物品,我建议使用泛型(恕我直言,无需重新发明轮子),即

uses ..., Generics.Collections;

type
  TMyNode = class(TObject)// you can leave this out if you like
    MyIntList: TList<Integer>; // you can do lookups, you have to implement your own saving/loading methods
    MyStringList: TStringList or TList<string>; // you can do lookups in both cases, use TStringList for save/load of data
  end;

现在我假设您希望存储虚拟树中的所有项目并稍后加载它们,您可以通过定义自己的文件结构来做到这一点,即

type
  TMyFileHeader = record
    CountItems: Integer; // number of items in the tree
    ...
  end;

const
  szMyFileHeader = SizeOf(TMyFileHeader);

type
  TMyItemEntry = record
    CountInt: Integer; // number of integer values
    ...
  end;

const
  szMyItemEntry = SizeOf(TMyItemEntry);

现在您需要实现加载和保存,我建议使用 TFileStream 进行保存和加载——非常简单,

伪代码,抱歉没时间写部分代码:-\

a) 保存内容:

  • 将项目数保存在 TMyFileHeader 变量中并将其写入文件

  • 对于树中的每一项,保存整数列表,保存字符串列表

b) 加载内容:

  • 读取文件头——让你知道需要从文件中读取多少项

  • 做一个 for Index := 0 to Count -1 从文件中读取项目

注意:您可以将每个项目的字符串列表直接保存到文件流中的当前位置,但是最好使用以下命令直接保存:

FileStream.WriteBuffer(PChar(AStringList.Text)^, Length(AStringList.Text) * SizeOf(Char));

希望对你有帮助,代码的实际实现由你自己决定,玩得开心!!

【讨论】:

  • 这并没有解决层级问题
  • @David Heffernan 根据他的“实际”需求,他可以做出妥协,如果他需要创建一百万个节点,使用对象会占用大量内存(现在这一切都归结为目标系统当然),但是唱片会吃得更少,非常欢迎ANYHU你来一个好的答案:-P
  • @Dorin 我对对象只字未提。对于它的价值,我认为记录很好。也就是说,对象不再使用内存 - TObject 没有成员字段。我的观点是问题在于层次结构,而不是如何将信息存储在节点中。
  • 我在我的问题中提到记录是很好的解决方案,但使用记录并不能解决大卫所说的一些问题。
  • @David 加油,别那么自卫:) 但是关于“也就是说,对象不再使用内存”——为什么有人会使用没有字段的对象?这同样适用于记录,但是如果您在对象中添加一个整数字段并在记录中添加一个整数字段,当您查看 .InstanceSize 时,您会看到不同之处,话虽如此,乘以项目(和子项目)的数量和你会浪费很多内存+创建和释放过程,这将需要相当多的 CPU 周期(如果有很多项目)。
【解决方案3】:

您可以使用 TXMLDocument。

如果您想更好地控制放入其中的内容,我建议您创建一个描述所需结构的 xsd,并使用 XML 数据绑定向导生成可以使用的 Delphi 代码。

这个架构

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:complexType name="itemType">
        <xs:sequence>
            <xs:element name="id" type="xs:int"/>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="itemlist" type="itemlistType" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="itemlistType">
        <xs:sequence>
            <xs:element name="item" type="itemType" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="itemlist" type="itemlistType"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

将为您提供在 delphi 中使用的这些接口

  IXMLRoot = interface(IXMLNode)
    ['{16C6C960-58B7-400C-9E46-7ACC7BEF276F}']
    { Property Accessors }
    function Get_Itemlist: IXMLItemlistType;
    { Methods & Properties }
    property Itemlist: IXMLItemlistType read Get_Itemlist;
  end;

{ IXMLItemlistType }

  IXMLItemlistType = interface(IXMLNodeCollection)
    ['{59F80BAC-887E-48DF-8288-95276BF9DCE7}']
    { Property Accessors }
    function Get_Item(Index: Integer): IXMLItemType;
    { Methods & Properties }
    function Add: IXMLItemType;
    function Insert(const Index: Integer): IXMLItemType;
    property Item[Index: Integer]: IXMLItemType read Get_Item; default;
  end;

{ IXMLItemType }

  IXMLItemType = interface(IXMLNode)
    ['{1218DD35-C3EF-40E6-831A-1A4AA0782C36}']
    { Property Accessors }
    function Get_Id: Integer;
    function Get_Name: WideString;
    function Get_Itemlist: IXMLItemlistType;
    procedure Set_Id(Value: Integer);
    procedure Set_Name(Value: WideString);
    { Methods & Properties }
    property Id: Integer read Get_Id write Set_Id;
    property Name: WideString read Get_Name write Set_Name;
    property Itemlist: IXMLItemlistType read Get_Itemlist;
  end;

【讨论】:

  • 有趣的解决方案,但我不是 TXMLDocument 的忠实粉丝,因为它是外部依赖项。
  • XML 对于树来说仍然是一个很好的结构,所以如果你不喜欢 TXMLDocument,你可以使用其他东西来访问和修改 XML 数据结构。
  • 我什至不想想象这个解决方案的缓慢性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-17
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多