我正在上课
class Group
{
public Collection<int> UserIds { get; set; }
public int CreateByUserId { get; set; }
public int HashKey { get; set; }
}
我想基于UsersIds[]
和CreateByUserId
生成一些唯一的哈希键,并将其存储到mongo并在其上进行搜索。
条件:
UsersIds[]
和CreateByUserId
UsersIds[]
中的用户数增加时,为此我倾向于GetHashCode()
功能:
public override int GetHashCode()
{
unchecked
{
var hash = (int)2166136261;
const int fnvPrime = 16777619;
List<int> users = new List<int>() { CreateByUserId };
UserIds.ToList().ForEach(x => users.Add(x));
users.Sort();
users.ForEach(x => hash = (hash * fnvPrime) ^ x.GetHashCode());
return hash;
}
}
这是一个更好的解决方案还是建议一些更好的解决方案。
答案 0 :(得分:1)
所以如果打算在数据库中保存哈希值,不要在对象上覆盖GetHashCode
,那就是与HashTables(Dictionary,HashSet ..)一起使用,而不是Equals
而不是足够独特,适合您的目的。而是使用已建立的哈希函数,例如SHA1。
public string Hash(IEnumerable<int> values)
{
using (var hasher = new SHA1Managed())
{
var hash = hasher.ComputeHash(Encoding.UTF8.GetBytes(string.Join("-", values)));
return BitConverter.ToString(hash).Replace("-", "");
}
}
用法:
var hashKey = Hash(UsersIds.Concat(new[]{ CreateByUserId });
如果需要,请排序UsersIds
。
答案 1 :(得分:0)
HashKey
是一个值,用于检查Equals()
的来电是否可能产生true
的结果。
如果元素可能是正确的元素,或者如果它确定为假元素,则使用该哈希键进行快速决策。
首先,将HashKey
替换为Unique Id
。
如果您想要一个唯一的ID,我建议您使用带有Id列的数据库,如果您将其存储在那里,然后使用其他数据获取Id。 +在mongo DB中,每个条目也都有自己的ID: 见here
mongo中的每个对象都有一个id,它们可以排序 插入顺序。获取用户集合有什么问题 对象,迭代它并将其用作递增的ID?[...]
这样:使用数据库获取唯一ID,并使用简单的廉价数学计算HashKey
(如果您不再需要它),例如添加用户ID。
以编程方式进行编写: 如果要以编程方式检查它并忽略数据库中的ID,则需要实现给定对象的GetHashKey() - Function和Equals() - Function。
class Group
{
public Collection<int> UserIds { get; set; }
public int CreateByUserId { get; set; }
public override bool Equals(object obj)
{
Group objectToCompare = (Group)obj;
if (this.UserIds.Count != objectToCompare.UserIds.Count)
return false;
if (this.CreateByUserId != objectToCompare.CreateByUserId)
return false;
foreach (int ownUserId in this.UserIds)
if (!objectToCompare.UserIds.Contains(ownUserId))
return false;
//some elements might be double, i.e. 1: 1,2,2 vs 2: 1,2,3 => not equal. cross check to avoid this error
foreach (int foreignUserId in objectToCompare.UserIds)
if (!this.UserIds.Contains(foreignUserId))
return false;
return true;
}
public override int GetHashCode()
{
int sum = CreateByUserId;
foreach (int userId in UserIds)
sum += userId;
return sum;
}
}
用法:
Group group1 = new Group() { UserIds = ..., CreateByUserId = ...};
Group group2 = new Group() { UserIds = ..., CreateByUserId = ...};
group1.Equals(group2);
Here is the answer to "Why do we need the GetHashCode-Function when we use Equals?"
注意:这肯定不是Equals()
- 方法中性能最佳的解决方案。根据需要进行调整。
答案 2 :(得分:0)
通常,如果没有关于数据的一些额外信息,则无法从一大堆其他整数中创建唯一的整数。如果对其允许值范围没有约束,则即使从单个long值也无法创建唯一的int键。
GetHashCode函数不保证您为每个可能的Group对象获取唯一的整数哈希键。但是,良好的散列函数会尝试最小化冲突 - 例如,为不同的对象生成相同的哈希码。在这个SO答案中有很好的哈希函数示例: What is the best algorithm for an overridden System.Object.GetHashCode?
通常,您需要GetHashCode将对象存储为字典和散列集中的键。与之前的回答一样,您需要为该情况重写Equals方法,因为字典和散列集之类的散列表通过在名为bucket的列表中存储具有相同散列码的项来解决冲突。他们使用Equals方法来识别存储桶中的项目。建议您在覆盖GetHashCode时覆盖Equals作为预防措施。
没有具体说明你期望从什么类型的平等&#39; Group&#39;对象。想象一下具有相同CreateByUserID和以下UserIds的两个对象:{1,2}和{2,1}。他们是平等的吗?或者订单很重要?
允许从任何地方更改群组字段不是一个好主意。我会用只读字段实现它:
class Group : IEquatable<Group>
{
private readonly Collection<int> userIds;
public ReadOnlyCollection<int> UserIds { get; }
public int CreateByUserId { get; }
public int HashKey { get; }
public Group(int createByUserId, IList<int> createdByUserIDs)
{
CreateByUserId = createByUserId;
userIds = createdByUserIDs != null
? new Collection<int>(createdByUserIDs)
: new Collection<int>();
UserIds = new ReadOnlyCollection<int>(userIds);
HashKey = GetHashCode();
}
public void AddUserID(int userID)
{
userIds.Add(userID);
HashKey = GetHashCode();
}
//IEquatable<T> implementation is generally a good practice in such cases, especially for value types
public override bool Equals(object obj) => Equals(obj as Group);
public bool Equals(Group objectToCompare)
{
if (objectToCompare == null)
return false;
if (ReferenceEquals(this, objectToCompare))
return true;
if (UserIds.Count != objectToCompare.UserIds.Count || CreateByUserId != objectToCompare.CreateByUserId)
return false;
//If you need equality when order matters - use this
//return UserIds.SequenceEqual(objectToCompare.UserIds);
//This is for set equality. If this is your case and you don't allow duplicates then I would suggest to use HashSet<int> or ISet<int> instead of Collection<int>
//and use their methods for more concise and effective comparison
return UserIds.All(id => objectToCompare.UserIds.Contains(id)) && objectToCompare.UserIds.All(id => UserIds.Contains(id));
}
public override int GetHashCode()
{
unchecked // to suppress overflow exceptions
{
int hash = 17;
hash = hash * 23 + CreateByUserId.GetHashCode();
foreach (int userId in UserIds)
hash = hash * 23 + userId.GetHashCode();
return hash;
}
}
}