自定义 IEqualityComparer 比使用 .Where() + .Any() 更快地从两个列表中获取不同的对象
Custom IEqualityComparer to get distinct objects from two lists faster than using .Where() + .Any()
所以我有两个对象列表,对象有多个字段,但我想仅根据其中两个来区分它们。
给你图片,对象KeyAndValue由字段Key和Tag组成,所以:
list1 = { obj1(key=1,tag=A), obj2(key=2,tag=A) }
list2 = { obj3(key=1,tag=A), obj4(key=2,tag=B) }
我目前正在使用:
list1.Where(x => !list2.Any(y => y.key == x.key)).ToList();
正确的结果是:obj1、obj2 和 obj4,因为 obj3 与 obj1 具有相同的键和标签
我想要完成的是加快这个过程,因为很多对象需要很长时间。我发现自定义 IEqualityComparer 在这里可以提供帮助,所以我基于 MS Specification 编写了自己的
它看起来像这样:
class KeyComparer : IEqualityComparer<KeyAndValue>
{
public bool Equals(KeyAndValue x, KeyAndValue y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.key == y.key && x.tag == y.tag;
}
public int GetHashCode(KeyAndValue keyAndValue)
{
if (Object.ReferenceEquals(keyAndValue, null))
return 0;
int hashKeyAndValueKey = keyAndValue.key == null ? 0 : keyAndValue.key.GetHashCode();
int hashKeyAndValueTag = keyAndValue.tag == null ? 0 : keyAndValue.tag.GetHashCode();
return hashKeyAndValueKey ^ hashKeyAndValueTag;
}
}
我是这样使用的:
list1.Except(list2, new KeyComparer()).ToList();
不幸的是,它只会从 list2 中删除重复项。似乎它甚至没有触及 list1,我不知道这是否是我的自定义比较器的错误,我使用它的方式或者我应该使用另一种方法。我一直在查看其他问题,但找不到有效的答案(或者至少是我实际上知道如何正确实施的答案)。
我认为你不需要Except
。您希望两者具有不同的值?
var distinctValues = list1.Union(list2).Distinct();
您需要在 KeyAndValue
中实现 GetHashCode
/Equals
或使用比较器按键和值比较对象。
(下面是老东西)
不确定我是否理解正确。难道是你没认出来Except new IEnumerable and ToList a new List?
尝试:
var list1AdditionalItems = list1.Except(list2, new KeyComparer()).ToList();
可能还有:
var list2AdditionalItems = list2.Except(list1, new KeyComparer()).ToList();
另一个观察。在此代码中:
list1.Where(x => !list2.Any(y => y.key == x.key)).ToList();
你只检查密钥。如果你想要这种行为,你应该相应地编写比较器:
class KeyComparer : IEqualityComparer<KeyAndValue>
{
public bool Equals(KeyAndValue x, KeyAndValue y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return x.key == y.key;
}
public int GetHashCode(KeyAndValue keyAndValue)
{
if (ReferenceEquals(keyAndValue, null))
return 0;
return keyAndValue.key == null ? 0 : keyAndValue.key.GetHashCode();
}
}
最后但同样重要的是:当您需要性能时,请考虑改用字典。
正如 Stefan Steinegger(我非常感谢他付出的努力和时间)在第一条评论中提到的,我的两种方法都没有 return obj4 我发现了问题并决定实施完全不同的方法。现在我的KeyAndValueclass也有一个int Hash字段,当调用构造函数时Hash字段被填充key.GetHashCode() ^ tag.GetHashCode()
。它简化了比较,因为现在我首先将 list1 与 list2 组合,然后通过发送它:CombinedList.GroupBy(x => x.Hash).Select(y => y.First()).ToList();
结果似乎是正确的:)
所以我有两个对象列表,对象有多个字段,但我想仅根据其中两个来区分它们。
给你图片,对象KeyAndValue由字段Key和Tag组成,所以:
list1 = { obj1(key=1,tag=A), obj2(key=2,tag=A) }
list2 = { obj3(key=1,tag=A), obj4(key=2,tag=B) }
我目前正在使用:
list1.Where(x => !list2.Any(y => y.key == x.key)).ToList();
正确的结果是:obj1、obj2 和 obj4,因为 obj3 与 obj1 具有相同的键和标签
我想要完成的是加快这个过程,因为很多对象需要很长时间。我发现自定义 IEqualityComparer 在这里可以提供帮助,所以我基于 MS Specification 编写了自己的 它看起来像这样:
class KeyComparer : IEqualityComparer<KeyAndValue>
{
public bool Equals(KeyAndValue x, KeyAndValue y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.key == y.key && x.tag == y.tag;
}
public int GetHashCode(KeyAndValue keyAndValue)
{
if (Object.ReferenceEquals(keyAndValue, null))
return 0;
int hashKeyAndValueKey = keyAndValue.key == null ? 0 : keyAndValue.key.GetHashCode();
int hashKeyAndValueTag = keyAndValue.tag == null ? 0 : keyAndValue.tag.GetHashCode();
return hashKeyAndValueKey ^ hashKeyAndValueTag;
}
}
我是这样使用的:
list1.Except(list2, new KeyComparer()).ToList();
不幸的是,它只会从 list2 中删除重复项。似乎它甚至没有触及 list1,我不知道这是否是我的自定义比较器的错误,我使用它的方式或者我应该使用另一种方法。我一直在查看其他问题,但找不到有效的答案(或者至少是我实际上知道如何正确实施的答案)。
我认为你不需要Except
。您希望两者具有不同的值?
var distinctValues = list1.Union(list2).Distinct();
您需要在 KeyAndValue
中实现 GetHashCode
/Equals
或使用比较器按键和值比较对象。
(下面是老东西)
不确定我是否理解正确。难道是你没认出来Except new IEnumerable and ToList a new List?
尝试:
var list1AdditionalItems = list1.Except(list2, new KeyComparer()).ToList();
可能还有:
var list2AdditionalItems = list2.Except(list1, new KeyComparer()).ToList();
另一个观察。在此代码中:
list1.Where(x => !list2.Any(y => y.key == x.key)).ToList();
你只检查密钥。如果你想要这种行为,你应该相应地编写比较器:
class KeyComparer : IEqualityComparer<KeyAndValue>
{
public bool Equals(KeyAndValue x, KeyAndValue y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return x.key == y.key;
}
public int GetHashCode(KeyAndValue keyAndValue)
{
if (ReferenceEquals(keyAndValue, null))
return 0;
return keyAndValue.key == null ? 0 : keyAndValue.key.GetHashCode();
}
}
最后但同样重要的是:当您需要性能时,请考虑改用字典。
正如 Stefan Steinegger(我非常感谢他付出的努力和时间)在第一条评论中提到的,我的两种方法都没有 return obj4 我发现了问题并决定实施完全不同的方法。现在我的KeyAndValueclass也有一个int Hash字段,当调用构造函数时Hash字段被填充key.GetHashCode() ^ tag.GetHashCode()
。它简化了比较,因为现在我首先将 list1 与 list2 组合,然后通过发送它:CombinedList.GroupBy(x => x.Hash).Select(y => y.First()).ToList();
结果似乎是正确的:)