【问题标题】:Is it possible to populate a lookup table at Compile Time? C#是否可以在编译时填充查找表? C#
【发布时间】:2011-05-19 21:50:39
【问题描述】:

我有一个相当大的哈希表,其中填充了仅查找静态数据。

这意味着当程序启动时,我要么有一个很长的初始化器/构造器方法,它将执行许多 hashtable.Add() 方法 (yuk),要么从我自定义生成的资源文件中反序列化编码时间。

我可以使用属性或其他方式在编译时包含这些数据吗?

【问题讨论】:

  • 也许你可以澄清一下是你写的代码量,还是你担心的代码速度?
  • 感谢 MerickOWA。两者都是。仅仅为了获取序列化数据而编写自定义程序是不优雅的。在运行时一次填充一个条目,编码更少,但会增加加载时间。
  • 我不认为你会在一个解决方案中找到小巧/快速/优雅的所有解决方案,除非它是专为此目的而设计的编程语言。

标签: c# .net collections hashtable


【解决方案1】:

如果您的数据是严格静态的(或者足够静态,您可以在程序中对其进行硬编码),那么您可以将所有值放在 switch 语句中。不能说这是否是一个好主意,但它似乎并不比加载 HashTable 更糟糕。从好的方面来说,“哈希表”初始化确实变成了严格的编译时操作:

public int Lookup(int key)
{
  switch (key)
  {
    case K1: return V1;
    case K2: return V2;
    case K3: return V3;
    case K4: return V4;
    case K5: return V5;
    case K6: return V6;
    case K7: return V7;
    default: return V_WHOOPS;
  }
}

如果值的数量很大,您可以编写一个脚本来生成代码,而不是手动输入。

【讨论】:

  • wageoghe,每次查找都执行约 500 个条件不是好的形式。对于 20 件以下的物品,可能。
  • 我认为实际上不会发生这么多条件。有关 switch 与 if 性能的讨论,请参阅这篇文章。 stackoverflow.com/questions/445067/if-vs-switch-speed
  • stackoverflow.com/questions/395618/if-else-vs-switch 这是另一篇文章(在 ima 的回答中),在发布模式下,switch 语句查找可以是 O(1)。我不能说 switch 语句会比 HashTable 快,但它应该很容易测试。如果它更快,请使用它。如果没有,您将回到原来的问题,即如何在运行时填充 HashTable。
  • 基于整数的 switch 语句是否编译为跳转表的一个关键方面是 case 值的密度。如果这些值是连续的或几乎是连续的,那么编译器选择跳转表的机会就会大得多。如果值到处都是,值之间的差距很大,那么就内存使用/代码大小而言,跳转表将过于昂贵。如果你能让它产生一个跳表,不管有多少情况,性能都是恒定的。
  • 您可能是对的,因为您的键是字符串。 FWIW,我做了一个粗略的测试,我创建了一个基于开关的查找函数,其中包含 500 个字符串案例和一个填充了 500 个字符串的 HashTable,然后为每个查找方法进行了 10,000,000 次查找(仅限有效键)。对于 10,000,000 次查找,switch 语句查找平均需要 3200 毫秒,而 HashTable 查找平均需要 2400 毫秒。因此,虽然基于开关的查找平均花费了 50% 的时间,但超过 10,000,000 次查找的总差异仅为 1200 毫秒。
【解决方案2】:

取决于您在表格中的内容。您始终可以使用资源文件。

http://msdn.microsoft.com/en-us/library/ekyft91f.aspx

【讨论】:

  • 我已经在问题中谈到了资源文件。你知道自动创建资源数据的方法吗?
  • 嗯,它是一个 xml 文件,因此您可以在预构建过程中设置一个选项来创建文件,然后编译它。这是我能想到的最好的方法。
【解决方案3】:

没有办法在编译时从概念上设置对象。该对象必须由 .NET 运行时分配/构造,然后以某种方式填充数据。

至于如何使这更快,您可以尝试在对哈希表进行所有缓慢的 Add 调用之后将其序列化为二进制文件。

然后在您的主应用程序中,您可以在需要时将其序列化回来。

与许多 .Add 调用相比,这有望让您更快地初始化 HashTable。

【讨论】:

    【解决方案4】:

    如果您希望在运行时创建一个 HashTable 实例,则必须在运行时分配和填充该 HashTable 实例。

    如果你想用一个值做出决定,你可以写一个方法。

    【讨论】:

      【解决方案5】:

      如果您不想在运行时加载 Hashtable,则可以处理此问题的方法是将数据存储在数据库中,然后使用查询或 LINQ 执行查找。如果您只想要面向桌面的东西,那么有几个选项, SQLLite SqlServerCE。您还可以根据应用程序的范围使用更强大的东西,例如 SqlServer 或 MySql。

      【讨论】:

      • 在运行时仍然会被填充。
      • 当然可以,但它不需要大量的 _values。在构造函数中添加调用。只是一种处理方式,如果列表会经常更改,我肯定不会对其进行硬编码。但如果它是一个永久列表,这将起作用。我不知道它会比从文件中加载资源的性能更低,即使您构建了集合并将其序列化然后从序列化版本加载它,您仍然必须填充集合。
      • HashSetHashTable 的概念不同。
      • @Nick 你是对的,我在 HashSet 上的错误,将删除该答案,因为我看不到在没有多次添加调用的情况下初始化字典的方法。
      • { x, y, z }Add(x) etc 相同,被问及替代方案。
      猜你喜欢
      • 2019-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-02
      • 2015-04-20
      • 2022-01-10
      相关资源
      最近更新 更多