简体   繁体   English

哈希由两个值组成的哈希元素中的查找元素

[英]Lookup elements in Hash where the key is composed of two values

I have to retrieve from database the following structure: 我必须从数据库中检索以下结构:

    User ID
    User Name
    First Name
    Last Name
    Employee Number

These fields I will store them in memory for fast access, in other words a cache. 这些字段我会将它们存储在内存中以便快速访问,换句话说就是缓存。 We are talking about 300,000 records in average. 我们正在谈论平均30万条记录。

My problem is that I have to create a structure for fast querying sometimes by User Name and some times by Employee Number. 我的问题是,我必须创建一个用于快速查询的结构,有时按用户名,有时按雇员编号。 NEVER by User ID, only by the two aforementioned fields. 永远不要通过用户ID,只能通过前面提到的两个字段。

In a Dictionary I am bounded to the ONLY ONE KEY principle, so ... 在词典中,我受制于“一键通”原则,所以...

- Is there a way to create a key that sort of combines User Name and Employee Number to use in a Dictionary? -是否有一种方法可以创建将用户名和员工编号结合在一起以在词典中使用的密钥?

The issue in here is that SOMETIMES I will have the User Name given by the user for lookup and sometimes I will have the Employee Number, but never both at the same time. 这里的问题是,有时我会获得用户指定的用户名以进行查找,有时我会获得员工编号,但永远不会同时使用。

So this key, lets say MyKey("user-name", "") and MyKey("", "employee-number") should retrieve the same registry from the map. 因此,假设该密钥说MyKey(“ user-name”,“”)和MyKey(“”,“ employee-number”)应该从映射中检索相同的注册表。

I want to avoid creating two Dictionaries in memory, one to search through User Name and other through Employee Number? 我想避免在内存中创建两个词典,一个通过用户名搜索,另一个通过员工编号搜索?

An alternative could be to store the result from the DB in a big list and then query with Linq. 一种替代方法是将来自数据库的结果存储在一个大列表中,然后使用Linq查询。 This however will be a search of O(n) and we are talking about performance here. 但是,这将是对O(n)的搜索,我们在这里讨论性能。

You have the following choices: 您有以下选择:

  1. Two dictionaries 两本字典
  2. A multidictionary (never used this but it looks very useful) 多字典(从未使用过,但看起来非常有用)
  3. Access the database directly 直接访问数据库

Choice may be down to testing each solution in a typical scenario. 选择可能取决于在典型情况下测试每个解决方案。 It needs the effort of come experimention. 需要实验的努力。

so I solved my problem creating a Key object with a Type and a Value. 所以我解决了创建带有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;
    }
}

After that I created a dictionary like 之后,我创建了一个字典

var cache = new Dictionary<MultiKey, User>();

And finally I added my keys and values to the dictionary like this: 最后,我将自己的键和值添加到字典中,如下所示:

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);
}

notes on performance Talking to a colleague, he was defending the two hash table technique instead of the approach I used with the MultiKey. 性能说明与同事交谈时,他捍卫了两个哈希表技术,而不是我与MultiKey一起使用的方法。 He was arguing that performance during search (access) with a string keys in two spearated hashes was 'faster' or 'more performant' than a single cache with a complex key. 他争辩说,与带有复杂键的单个缓存相比,在带有两个长叉哈希的字符串键进行搜索(访问)期间的性能“更快”或“更高效”。 His argument was that collisions tend to happen more ofthen when the cache is bigger/complex. 他的论点是,当缓存更大/更复杂时,冲突往往会更多。 I would like to hear your opinion. 我想听听您的意见。 In the end I used this approach and it works. 最后,我使用了这种方法,并且有效。

For accessing items in the cahe it is necessary to provide the MultiKey object or a means to recreate it. 为了访问cahe中的项目,有必要提供MultiKey对象或重新创建它的方法。 In that sense I created the following helper method 从这个意义上讲,我创建了以下辅助方法

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;
}

And I use it like this: 我这样使用它:

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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM