【问题标题】:C# non-evil fast array lookup?C#非邪恶快速数组查找?
【发布时间】:2011-07-09 21:19:27
【问题描述】:

我想让大量的类实例返回相同的相似数据字段,就像在这个示例实现中一样:

foreach (SomeClass sc in SomeClasses)
{
    System.Console.WriteLine(sc.GetData("1st field"));
    System.Console.WriteLine(sc.GetData("Another field"));
    System.Console.WriteLine(sc.GetData("and another"));
}

// ---- inside SomeClass:

Dictionary<string, string> myData;

public string GetData(string field)
{
    return myData[field];
}

我不喜欢示例中必须反复发生的字符串散列、查找和匹配(我假设 Dictionary 的工作原理)。我真的很想找到更好的方法。

来自 C 世界,我想为所有字段分配一个唯一的整数键,以便我可以更改为数组查找:

// ---- inside SomeClass:

string[] MyData;

public string GetData(int field_key)
{
    return MyData[field_key];
}

现在字段查找很高效,但在“数组是邪恶的”时代感觉不太对劲,而且处理 field_key 整数既繁琐又容易出错。

我不知道我是不是在这里追逐性能鬼,只是我想找到一个既高效又干净的设计。

建议?

【问题讨论】:

    标签: c# arrays performance dictionary


    【解决方案1】:

    您为什么不想要字典查找?字典的一个非常有效的实现是对数组中的哈希进行索引查找。因此,底层实现可以归结为第二个示例中的代码。这将使它成为 O(1)

    使用字典

    【讨论】:

    • 是的,我可以让字典查找整数键。我想这将是一个解决方案。
    • 问题:字段也用一个类来表示。让字典查找此类的对象是个好主意/可能吗?
    • @Larsp 这是问题所在。经验法则是永远不要预先优化。通过 Profiler 运行代码,甚至还有一些免费的。在您分析应用程序之前,您正在追逐幽灵。字典非常高效。
    • @Andrew,但是计算哈希取决于字符串的大小,所以不是 O(1)。
    • @Andrew,这正是使用 Big-O 计算复杂性的工作原理。如果计算所需的时间取决于可能无限的事物,则不能说某事物是 O(1)。并且可以缓存哈希,但string.GetHashCode() 不是这样实现的。
    【解决方案2】:

    因为这些字段在编译时是未知的,而是动态的和用户可配置的,所以我将稍微修改您的示例程序以使用一组属性。然后我会提倡一种与您类似的方法,但使用您自己的自定义类(此处称为 MyProperty)而不是字符串。性能至少与字符串方法一样好(也许比字符串方法好一点),但好处是它为您提供了更大的灵活性:如果您最终出于性能原因决定需要使用数组或列表方法,您可以轻松地将数组索引嵌入到您的 MyProperty 类中。您必须更改 GetData 的实现,而不是您的调用代码。

    public static void Test1() {
      SomeClass[] SomeClasses; //created somehow
    
      //in real life, this would be determined dynamically
      var properties=new[] {SomeClass.FirstField, SomeClass.AnotherField, SomeClass.AndAnother};
    
      foreach(var sc in SomeClasses) {
        foreach(var property in properties) {
          Console.WriteLine(sc.GetData(property));
        }
      }
    }
    
    public class SomeClass {
      public static readonly MyProperty FirstField=new MyProperty();
      public static readonly MyProperty AnotherField=new MyProperty();
      public static readonly MyProperty AndAnother=new MyProperty();
    
      private readonly Dictionary<MyProperty, string> myData=new Dictionary<MyProperty, string>();
    
      public string GetData(MyProperty property) {
        return myData[property];
      }
    }
    
    //default implementation of Equals and GetHashCode are fine here
    public class MyProperty {}
    

    但是,由于您的目标应用程序实际上是关于收集一组动态和用户可配置的属性 getter,也许您真的想制作一些 Funcs?像下面这样的代码会非常快,而且它仍然有你想要的能力,即它允许你创建一个小的动态的、用户可配置的属性 getter 列表。

    public static void Test2() {
      SomeClass[] SomeClasses; //created somehow
    
      //in real life, this would be determined dynamically
      var getters=new[] {SomeClass.FirstField, SomeClass.AnotherField, SomeClass.AndAnother};
      foreach(var sc in SomeClasses) {
        foreach(var getter in getters) {
          System.Console.WriteLine(getter(sc));
        }
      }
    }
    
    public class SomeClass {
      public static readonly Func<SomeClass, string> FirstField=sc => sc.field0;
      public static readonly Func<SomeClass, string> AnotherField=sc => sc.field1;
      public static readonly Func<SomeClass, string> AndAnother=sc => sc.field2;
    
      private string field0;
      private string field1;
      private string field2;
    }
    

    【讨论】:

    • 感谢您的回复和代码示例。我确实有一个代表字段/属性的类,我确实想知道是否可以使用该类的实例进行字典查找而不会遇到麻烦。我没有想到 func 解决方案,非常有启发性,谢谢!对我来说,学习 C# 与解决实际问题一样重要。
    【解决方案3】:

    如果您的实例具有相同的字段,为什么不直接使用属性?

    foreach (SomeClass sc in SomeClasses)
    {
        System.Console.WriteLine(sc.FirstField);
        System.Console.WriteLine(sc.AnotherField);
        System.Console.WriteLine(sc.AndAnother);
    }
    

    【讨论】:

    • 这些字段是未知的编译时间。它们是动态的和用户可配置的。
    【解决方案4】:

    首先,如果您不确定这是否真的是您的性能问题,那么是的,您正在追逐性能幽灵,并且您当前的实现很好。

    但是,如果您在分析过程中发现您确实需要加快此代码的速度,那么您看起来还不错。 “数组是邪恶的”大多数情况下只在公共接口中是正确的,可以将它们用于实现。

    不过,我会更改您的代码的一件事:创建一个包含字段的enum,并使用它来代替int。它同样快速且更具可读性。如果在编译时这些字段是未知的,使用int 就可以了。如果您在编译时确实知道某些字段,则可以为它们使用静态属性。

    【讨论】:

    • 实际上,我确实为非动态字段创建了一个枚举,只是编译器要求我在查找数组之前将枚举成员强制转换为 int 让我很恼火:)
    • @Larsp:是的,这很烦人。您可以使用一堆公共常量创建一个静态类,但编译器不会检查冲突。对此没有完美的答案。 (我猜代码生成器可以从一个简单的标识符列表中创建具有int 类型的常量,而不会重叠,但这会使构建过程更加复杂)
    • 具有公共常量的静态类是一个合理的选择,感谢您的提示。
    猜你喜欢
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 2023-04-11
    • 2010-10-02
    • 2016-11-18
    • 2011-01-02
    • 1970-01-01
    相关资源
    最近更新 更多