LINQ Distinct计数重复,即使哈希代码相同



我正在按RegEx模式对日志记录进行分组。在对它们进行分组之后,我想获得每组的记录的Distinct计数。在本例中,Distinct被定义为相同的访问密钥和相同的年、月、日、小时和分钟。

这只是一种更准确地统计不同消费者记录的内容的方法。

好吧,所以我把它们分组如下:

var knownMessages = logRecords
    .Where(record => !string.IsNullOrEmpty(record.InclusionPattern))
    .GroupBy(record => new
    {
        MessagePattern = record.InclusionPattern
    })
    .Select(g => new KnownMessage
    {
        MessagePattern = g.Key.MessagePattern,
---->   Count = g.Distinct().Count(),
        Records = g.ToList()
    })
    .OrderByDescending(o => o.Count);

类型的GetHashCode是这样实现的:

public override int GetHashCode()
{
    var visitKeyHash = this.VisitKey == null ?
        251 : this.VisitKey.GetHashCode();
    var timeHash = this.Time.Year + this.Time.Month + this.Time.Day +
        this.Time.Hour + this.Time.Minute;
    return ((visitKeyHash * 251) + timeHash) * 251;
}

但是,例如,在列表中,我有三条记录返回相同的哈希代码1439926797;我仍然得到3的计数。我知道它正在利用GetHashCode(正如我所期望的)来进行比较,因为我在那里有一个断点来查看哈希代码是什么

我错过了什么?

首先让我重复一下我在评论中所说的话。

逻辑是:如果a.GetHashcode() != b.GetHashCode()然后a != b,如果a.GetHashCode() == b.GetHashCode() && a.Equals(b)然后a == b,那么所有GetHashcode()为您所做的就是让您跳过Equals()检查,如果您有两个不同的值。这就是为什么你需要同时实现这两个。如果你只实现Equals(),那么a.GetHashCode() == b.GetHashCode()步骤就会失败,它永远不会尝试你实现的Equals()。

GetHashCode()应该很快,并且当它位于依赖于它的值的集合中时,它的值不应该改变。因此,如果要将VisitKeyTime存储在DictionaryHashSet或类似文件中,请不要修改它们。

所以你所需要做的就是:

public override int GetHashCode()
{
    var visitKeyHash = this.VisitKey == null ?
        251 : this.VisitKey.GetHashCode();
    var timeHash = this.Time.Year + this.Time.Month + this.Time.Day +
        this.Time.Hour + this.Time.Minute;
    return ((visitKeyHash * 251) + timeHash);
}
public override bool Equals(object obj)
{
    //Two quick tests before we start doing all the math.        
    if(Object.ReferenceEquals(this, obj))
        return true;
    KnownMessage message = obj as KnownMessage;
    if(Object.ReferenceEquals(message, null)))
        return false;
    return this.VisitKey.Equals(message.VisitKey) &&
           this.time.Year.Equals(message.Time.Year) &&
           this.time.Month.Equals(message.Time.Month) &&
           this.time.Day.Equals(message.Time.Day) &&
           this.time.Hour.Equals(message.Time.Hour) &&
           this.time.Minute.Equals(message.Time.Minute);
}

您似乎没有重写Equals方法,以便使用与哈希代码生成算法相同的相等定义。由于这是用来解决散列冲突的,所以两者始终保持同步是很重要的。

您不提供Equals覆盖。与其他基于哈希的集合(如DictionaryHashSet)一样,Distinct()使用的内部结构使用GetHashCode()来选择要存储的哈希,但使用Equals来确定实际的相等性。

问题可能是EqualsGetHashCode中的错误,但在后一种情况下,它与Equals不正确匹配(GetHashCode必须为Equals返回true的两个对象返回相同的散列,但当然也可以为两个不同的对象返回相同散列),这使它成为这对方法中的错误。因此,无论哪种方式,问题都直接或间接存在于您对Equals的覆盖中。

最新更新