【问题标题】:which one to use linked list or static arrays?哪一个使用链表或静态数组?
【发布时间】:2010-09-29 03:21:24
【问题描述】:

我在 C 中有一个类似于数据库表记录的结构。 现在当我使用 select 查询表时,我不知道我会得到多少条记录。 我想将选择查询返回的所有记录存储在我的结构数据类型的数组中。

哪种方法最好?

方法一:查找数组大小并分配

  1. 首先通过 select count(*) from table 获取记录数
  2. 分配一个静态数组
  3. 运行 select * from table,然后将每个记录循环存储在我的结构中。

方法二:使用单链表

while ( records returned )
{
    create new node 
    store the record in node 
}

哪种实现最好?

我的要求是,当我拥有所有记录时, 我可能会复制它们或其他东西。 但我不需要随机访问,也不会对特定记录进行任何搜索。

谢谢

【问题讨论】:

    标签: c linked-list


    【解决方案1】:

    'select count(*)' 的一个问题是值可能会在调用之间发生变化,因此您的“真实”选择将有许多与您期望的计数不同的项目。

    我认为最好的解决方案是您的“2”。 我会亲自分配一个数组(根据需要重新分配),而不是链表。这在支持增长数组的语言中更容易(例如 C++ 中的 std::vector<myrecord> 和 C# 中的 List<myrecord>)。

    【讨论】:

    • 是的,我考虑过这个问题..u 提到计数值可以改变...如果有人插入新记录.. 所以是的..我也觉得没有 2 更好..谢谢跨度>
    • 您肯定将 SELECT 计数 (*) 和“真正的”SELECT 放入可序列化事务中吗?因此,调用之间的值不会发生变化。
    【解决方案2】:

    您忘记了选项 3,它有点复杂,但它可能最适合您的特定情况。这是它通常在 C++ std::vector 中完成的方式。

    分配一个任意大小的数组。当该数组被填充时,分配一个新的更大的数组,其大小是填充数组的 1.5 到 2 倍,然后将填充的数组复制到这个数组。释放原始数组并用新数组替换它。起泡、冲洗、重复。

    【讨论】:

    • 这个算法在 C 中运行良好,我刚才提到了 C++ 作为例子。
    • 将所有内容也粘贴到不透明的数据结构中,并且只能通过定义的方法/宏访问该结构。
    • Qt 的文档提供了一种智能的数组分配+增长方法:doc.trolltech.com/4.4/containers.html#growth-strategies
    • 另外,由于 OP 使用的是 C,他可以使用 realloc() 而不是分配新数组并复制。这就是 Qt 的增长策略高效的原因。
    【解决方案3】:

    我忘记了选项 #4。分配一个固定大小的数组。当该数组已满时,分配另一个。您可以通过将数组链接到链表中来跟踪数组,或者使用更高级别的数组来保存指向数据数组的指针。当您需要随机访问时,这种两级方案非常有用,您只需将索引分成两部分即可。

    【讨论】:

      【解决方案4】:

      应该提出很多可能的批评。

      1. 您根本不是在谈论静态数组 - 静态数组将具有在编译时固定的预先确定的大小,并且可以是源文件本地的,也可以是函数本地的。你说的是动态分配的数组。

      2. 您没有给出任何记录大小或记录计数的指示,也没有说明底层数据库的动态程度(也就是说,在您的进程运行时,任何其他进程是否会更改任何数据)。尺寸信息并不是非常关键,但另一个因素是。如果您正在做某种报告,那么将数据提取到内存中就可以了;您不会修改数据库,并且数据是准确的快照。但是,如果在您修改记录时其他人可能正在修改记录,您的大纲解决方案就是如何丢失其他人更新的主要示例。这是一件糟糕的事情!

      3. 为什么你需要一次在内存中的所有数据?忽略大小限制,与按正确顺序处理每个相关记录一次相比,这到底有什么好处?您会看到,DBMS 为能够选择相关记录(WHERE 子句)和相关数据(SELECT 列表)并允许您指定序列(ORDER BY 子句)付出了很多努力,它们拥有最好的排序系统负担得起(比你或我可能生产的更好)。

      4. 如果您以块的形式分配数组,请注意二次行为。每次重新分配时,很有可能必须将旧内存复制到新位置。这将使您的记忆碎片化(旧位置将可供重用,但根据定义将太小而无法重用)。 Mark Ransom 指出了一个合理的替代方案——总体上不是世界上最简单的方案(但它避免了我提到的二次行为)。当然,您可以(并且会)通过一组合适的函数将其抽象出来。

      5. 批量提取(Mark Ransom 也提到过)也很有用。您可能希望预先分配批量提取所提取的数组,这样您就不必进行额外的复制。不过,这只是线性行为,因此不太严重。

      【讨论】:

        【解决方案5】:

        创建一个数据结构来表示您的数组或列表。假设您使用 OO 语言,并为您需要的一切创建访问器和构造器。在该数据结构中,保留一个数组,并且正如其他人所说,当数组被填满时,分配一个 2 倍大的新数组并复制到其中。只能通过您定义的例程访问该结构。

        Java 和其他语言就是这样做的。在内部,这甚至是 Perl 在 C 中的实现方式。

        我想说你最好的选择是寻找一个已经这样做的库......也许你可以借用 Perl 的这种数据结构的 C 实现。我敢肯定,它比您或我可以从头开始的任何东西都经过了更好的测试。 :)

        【讨论】:

          【解决方案6】:
          while(record = get_record()) {
            records++;
            records_array = (record_struct *) realloc(records_array, (sizeof record_struct)*records);
            *records_array[records - 1] = record;
          }
          

          这只是一个例子——请不要在生产环境中使用 realloc()。

          【讨论】:

          • 如果涉及任何其他内存分配活动(例如,在数据库访问例程中),这很容易导致二次行为,因为数据在重新分配时被复制 - 这甚至可能是副本每次迭代。
          • 乔纳森,你说得对。这是概念验证代码,我什至根本不会在生产中使用 realloc() 函数。
          【解决方案7】:

          链表是一个不错的简单选项。我会同意的。如果您更喜欢不断增长的数组,您可以在 Dave Hanson 的 C Interfaces and Implementations 中找到一个实现,作为奖励,它还提供了链表。

          在我看来,这就像一个设计决策,可能会随着您的应用程序的发展而改变,因此您绝对应该将表示隐藏在合适的 API 后面。如果你还不知道怎么做,Hanson 的代码会给你一些很好的例子。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-03-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多