Hash中的查找元素,其中键由两个值组成

时间:2014-07-07 07:55:33

标签: c# performance linq dictionary

我必须从数据库中检索以下结构:

    User ID
    User Name
    First Name
    Last Name
    Employee Number

这些字段我将它们存储在内存中以便快速访问,换句话说就是缓存。我们平均在谈论300,000条记录。

我的问题是我必须创建一个结构,以便有时通过用户名快速查询,有时还可以通过员工编号进行快速查询。绝不是用户ID,只能通过上述两个字段。

在字典中,我受限于唯一的一键原则,所以......

- 有没有办法创建一个将用户名和员工号结合起来用于词典的键?

这里的问题是,有时我将使用用户提供的用户名进行查找,有时候我会有员工编号,但不会同时使用。

所以这个密钥,让我们说MyKey(“用户名”,“”)和MyKey(“”,“employee-number”)应该从地图中检索相同的注册表。

我想避免在内存中创建两个词典,一个用于搜索用户名,另一个用于通过员工编号进行搜索?

另一种方法是将数据库中的结果存储在一个大列表中,然后使用Linq进行查询。然而,这将是对O(n)的搜索,我们在这里谈论性能。

2 个答案:

答案 0 :(得分:0)

您有以下选择:

  1. 两本词典
  2. 一个多字典(从未使用过,但看起来很有用)
  3. 直接访问数据库
  4. 选择可能取决于在典型情况下测试每个解决方案。它需要来实验的努力。

答案 1 :(得分:0)

所以我解决了创建一个带有Type和Value的Key对象的问题。

/// <summary>
/// Represents a composite key for cached objects
/// </summary>
public class MultiKey
{
    /// <summary>
    /// The type of key
    /// </summary>
    public enum Type
    {
        /// <summary>
        /// The key represents a User Name
        /// </summary>
        UserName,

        /// <summary>
        /// The key represents an Employee Number
        /// </summary>
        EmployeeNumber
    }

    /// <summary>
    /// Gets or sets the Type of the Key.
    /// </summary>
    public Type KeyType { get; set; }

    /// <summary>
    /// Gets or sets the value of the Key
    /// </summary>
    public string Key { get; set; }

    /// <summary>
    /// Compare based on hash code
    /// </summary>
    /// <param name="obj">the object to compare against</param>
    /// <returns>true if both objects are equals, false otherwise</returns>
    public override bool Equals(object obj)
    {
        if (obj is FormCacheKey)
        {
            return (obj as FormCacheKey).GetHashCode() == this.GetHashCode();
        }

        return false;
    }

    /// <summary>
    /// Compares based on hash code
    /// </summary>
    /// <param name="p1">left side of the operator</param>
    /// <param name="p2">right side of the operator</param>
    /// <returns>true if both items are equal, false otherwise</returns>
    public static bool operator ==(FormCacheKey p1, FormCacheKey p2)
    {
        if ((object)p1 == null && (object)p2 == null)
        {
            return true;
        }
        if ((object)p1 == null || (object)p2 == null)
        {
            return false;
        }
        return p1.Equals(p2);
    }

    /// <summary>
    /// Compares based on hash code
    /// </summary>
    /// <param name="p1">left side of the operator</param>
    /// <param name="p2">right side of the operator</param>
    /// <returns>true if both items are different, false otherwise</returns>
    public static bool operator !=(FormCacheKey p1, FormCacheKey p2)
    {
        return !(p1 == p2);
    }

    /// <summary>
    /// Returns a hash key code that identifies this object
    /// </summary>
    /// <returns>The hash code.</returns>
    public override int GetHashCode()
    {
        const int CoPrimeNumber = 37;
        var finalHashCode = 17;

        finalHashCode = (finalHashCode * CoPrimeNumber) + this.KeyType.GetHashCode();
        finalHashCode = (finalHashCode * CoPrimeNumber) + this.Key.GetHashCode();

        return finalHashCode;
    }
}

之后我创建了一个像

这样的字典
var cache = new Dictionary<MultiKey, User>();

最后我将我的键和值添加到字典中,如下所示:

foreach (var user in users)
{
    var userNameKey = new MultiKey { KeyType = MultiKey.Type.UserName, Key = user.UserName };
    cache.Add(userNameKey, user);
    var employeeNumberKey = new MultiKey { KeyType = MultiKey.Type.EmployeeNumber, Key = user.EmployeeNumber };
    cache.Add(employeeNumberKey, user);
}

有关效果的说明 与同事交谈,他正在捍卫两种哈希表技术,而不是我使用MultiKey的方法。他认为在搜索(访问)期间使用两个特殊哈希值中的字符串键的性能更快&#39;或者“性能更高”&#39;而不是具有复杂密钥的单个缓存。他的论点是,当缓存更大/更复杂时,碰撞往往会发生更多。我想听听你的意见。最后我使用了这种方法并且有效。

要访问cahe中的项目,必须提供MultiKey对象或重新创建它的方法。从这个意义上讲,我创建了以下辅助方法

private T GetFromCache<T>(CacheKey.Type type, string key)
{
    var cKey = new MultiKey { KeyType = type, Key = key };
    T item;
    cache.TryGetValue(cKey, out item);

    return item;
}

我这样使用它:

public User GetUserByUserName(string userName)
{
    return this.GetFromDictionary<User>(MultiKey.Type.UserName, userName);
}

public User GetIndividualByEmployeeNumber(string employeeNumber)
{
    return this.GetFromDictionary<User>(MultiKey.Type.EmployeeNumber, employeeNumber);
}