【问题标题】:c# Creating unique keys from four numbers and checking duplicatesc# 从四个数字创建唯一键并检查重复项
【发布时间】:2021-03-18 10:48:32
【问题描述】:

我有很多 4 个数字的列表,[0,1,2,1], [5,1,3,1], [0,1,2,0]. [0,1,2,1],... 列表包含重复项。

我正在逐步迭代列表并检查是否已经迭代了相同的列表。在大写的情况下,第一个和最后一个列表是相同的。

目前我通过将列表添加到 HashSet 来做到这一点:

HashSet<string> visited = new HashSet<string>();
string key = string.Joint(";", listOfNumbers);

if(visited.Contains(key)){
   dosomething...
   visited.Add(key);
}

我注意到将整数转换为字符串并检查哈希集中是否存在此类字符串对于大型集合可能会很慢。如果我改用整数,即 int keys = (number1) * 1000000 + (number2) * 10000 + (number3) * 100 + (number4);,它会快 1/3,但这个键对于大数来说不一定是唯一的。

我认为,我缺少一些为数字列表创建唯一键并以某种方式检查重复项的基本知识。有没有比使用字符串更有效的方法来解决这个问题?

【问题讨论】:

  • 您的数字可以有多大?如果它可以大于某个数字,那么就没有唯一键。
  • 您已经理解了GetHashCode 的用途,所以请多阅读!最好的解决方案是使用为您的数组生成合适的哈希码的数据结构,而无需将其转换为字符串的成本。一个 4 元素元组可以工作(例如HashSet&lt;(int a, int b, int c, int d)&gt;),或者一个包装每个 4 元素数组的自定义结构。也不要使用.Contains 然后.Add:.Add 返回一个值,说明 HashSet 是否已经包含该值,所以使用它
  • 整个哈希码系统已经有了最初使用单个 int 作为查找的逻辑,然后如果你得到两个具有相同 int 查找的元素,则回退到正确检查相等性。这就是HashSet&lt;T&gt; 已经在做的事情。值得学习如何正确使用它。
  • 这就是 IequalityComparer(参见:docs.microsoft.com/en-us/dotnet/api/…)同时具有哈希和比较方法的原因。
  • 在 .NET 5 中你可以使用HashCode.Combine

标签: c#


【解决方案1】:

您可以实现自定义IEqualityComparer&lt;IList&lt;T&gt;&gt; 来检查重复的列表/数组:

public class ListComparer<T>: IEqualityComparer<IList<T>>
{
    private IEqualityComparer<T> _comparer;

    public ListComparer(IEqualityComparer<T> comparer = null)
    {
        _comparer = comparer ?? EqualityComparer<T>.Default;
    }

    public bool Equals(IList<T> list1, IList<T> list2)
    {
        if(list1 == null && list2 == null) return true;
        if(list1 == null || list2 == null) return false;
        return list1.SequenceEqual(list2, _comparer);
    }

    public int GetHashCode(IList<T> obj)
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = 17;
            foreach(T x in obj)
            {
                hash = hash * 23 + _comparer.GetHashCode(x);
            }
            return hash;
        }
    }
}

您可以在许多 LINQ 方法或字典中使用此比较器(如果您想将列表作为键添加)或在您的 HashSet&lt;T&gt; 中使用它:

List<int[]> lists = new List<int[]>
{
    new[]{0,1,2,1}, new[]{5,1,3,1}, new[]{0,1,2,0}, new[]{0,1,2,1}
};

HashSet<IList<int>> visited = new HashSet<IList<int>>(new ListComparer<int>());
foreach(var arr in lists)
{
    if(!visited.Add(arr))
    {
        Console.WriteLine($"Duplicate detected: {string.Join(",", arr)}");
        // do something ...
    }
}

输出:Duplicate detected: 0,1,2,1

【讨论】:

  • @PetrasVestartas:我刚刚注意到一个小问题并修复了它:使用SequenceEqual 的比较器:return list1.SequenceEqual(list2, _comparer);。如果您将自定义比较器传递给此类,那将是一个问题,因为GetHashCode 使用了它,但没有使用Equals。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-08-05
  • 2016-09-29
  • 1970-01-01
  • 2021-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多