内联哈希集合<>声明与静态属性,.Contains()性能

时间:2013-11-05 18:28:53

标签: c# c#-4.0 hashset

为了使用Contains方法,更好的(有什么区别),用HashSet声明一个静态字段或将其声明为内联(new HashSet {SomeEnum.SomeValue1,SomeEnum.SomeValue2,...} .Contains(SomeEnum) .SomeValue1))

我问这是因为在某些情况下我只会使用一次hashset,对我来说最好是在代码上而不是在某些静态属性中

示例内联(我想要使用的内容):

public void Validate(Type type) {
    if(!new HashSet<Type> { Type.TYPE_1, Type.TYPE_2, Type.TYPE_3, Type.TYPE_4 }.Contains(type)) {
        //do something
    }
    if(new HashSet<Type> { Type.TYPE_2, Type.TYPE_3, Type.TYPE_4, Type.TYPE_5 }.Contains(type)) {
        //do something
    }
}

静态示例(我不想使用):

private static HashSet<Type> _values1 = new HashSet<Type> { Type.TYPE_1, Type.TYPE_2, Type.TYPE_3, Type.TYPE_4 };
private static HashSet<Type> _values2 = new HashSet<Type> { Type.TYPE_2, Type.TYPE_3, Type.TYPE_4, Type.TYPE_5 };
public void Validate(Type type) {
    if(!_values1.Contains(type)) {
        //do something
    }
    if(_values2.Contains(type)) {
        //do something
    }
}

使用逻辑表达式的示例(我不想使用的内容):

public void Validate(Type type) {
    if(type != Type.TYPE_1 && type != Type.TYPE_2 && type != Type.TYPE_3 && type != Type.TYPE_4) {
        //do something
    }
    if(type == Type.TYPE_2 || type == Type.TYPE_3 || type == Type.TYPE_4 || type == Type.TYPE_5) {
        //do something
    }
}

3 个答案:

答案 0 :(得分:2)

如果您还没有通过性能测试将其识别为瓶颈,那么“正确”的方法就是使用对人们来说最有意义的代码。这有点主观,所以可能没有“正确”的方式,但任何不容易理解的方法都是“错误的”方式。

我可能只是使用内联声明的数组,除非值列表在其他方法中可重用,或者它很长,以至于它妨碍了读取方法尝试做的事情。

public void Validate(Type type) {
    if(!new[] { Type.TYPE_1, Type.TYPE_2, Type.TYPE_3, Type.TYPE_4 }.Contains(type)) {
        //do something
    }
}

如果您已将此确定为明确的性能瓶颈(意味着您可能每秒检查数百万次,那么您可能希望对几种不同的方法进行性能测试,因为正确的答案取决于您尝试匹配的集合中有多少项目。

除了您建议的方法之外,还有一些其他可能可能更快的可能性(但同样,您需要对它们进行测试以确保:

Flags Enum

看起来你正在使用枚举值。如果该枚举类型具有少量潜在值,则可以将其设置为标志枚举,然后使用按位逻辑在单个CPU操作中确定给定值是否与您要查找的任何值匹配。

[Flags]
public enum Type
{
    TYPE_1 = 1,
    TYPE_2 = 1<<1,
    TYPE_3 = 1<<2,
    TYPE_4 = 1<<3,
    TYPE_5 = 1<<4,
    // etc...
}

用法:

const Type toMatch = (Type.TYPE_1 | Type.TYPE_2 | Type.TYPE_3 | Type.TYPE_4);
if((type & toMatch) == 0)
{
    // do something
}

切换声明

编译器非常擅长弄清楚什么是最快的方法,所以如果你使用switch语句,它可以决定是否将其编译为一系列if / else检查,HashSet风格的方法或跳转表,取决于您要检查的项目的数量和值。

switch(type)
{
    case Type.TYPE_1:
    case Type.TYPE_2:
    case Type.TYPE_3:
    case Type.TYPE_4:
        break;
    default:
        // do something
        break;
}

答案 1 :(得分:0)

如果不添加新项目,那么静态方法是正确的方法

private static HashSet<Type> _values = new HashSet<Type> { Type.TYPE_1, Type.TYPE_2, Type.TYPE_3, Type.TYPE_4 };
public void Validate(Type type) {
    if(!_values.Contains(type)) {
        //do something
    }
}

System.Collections命名空间中的所有集合都是线程安全的for read only operations,因此这是一种完全可以接受的方法。

如果你以“首选”的方式做到这一点它仍然可以工作,但是你每次调用函数时都会重新创建集合,这是一个不必要的开销,肯定会损害性能。

答案 2 :(得分:0)

正如文章暗示的那样,记住查找的“偶尔”或“一次”使用情况。

如果查找仅在单个方法中使用,那么我将使用内联方法,但是我会使用数组(我实际上经常这样做)。我可能会或可能不会使用中间(本地)变量,具体取决于我是否使代码更清晰或更容易适应未来。

我不会使用静态(甚至是实例)变量,因为:

  1. 在这种情况下,序列不共享;
  2. 创建对象(特别是数组)的大量资源很少;
  3. GC对于短期对象非常好..更重要的是,无需延长生命周期
  4. 如果我想在多个方法之间共享此查找,我会考虑创建一个返回 new 对象的getter(在满足新要求时覆盖#2和#3)。使用局部变量,单个方法每次调用只会创建一个新的查找。

    我通常使用数组,因为:

    1. 语法略微更简单(可以推断并省略类型);
    2. 数组(尤其是构造)比HashSet更轻量级,但提供了所需的查找功能;
    3. 搜索小数组可能足够快(因为n非常小)
    4. 如果它使特定代码更难以遵循,我不会使用各种长手形式。任务是“包含”,而不是一些更复杂的条件逻辑。

      除非存在实际的性能问题,否则请使用干净简单的方式继续执行更多有趣的任务。