【问题标题】:C#: Compare two ArrayList of custom class and find duplicatesC#:比较自定义类的两个 ArrayList 并查找重复项
【发布时间】:2016-09-12 19:14:10
【问题描述】:

我有两个 ArrayList 数组。

public class ProductDetails
{
    public string id;
    public string description;
    public float rate;
}

ArrayList products1 = new ArrayList();
ArrayList products2 = new ArrayList();
ArrayList duplicateProducts = new ArrayList();

现在我想要的是让所有产品(包含 ProductDetails 类的所有字段)在 products1products2 中都有重复的描述。

我可以像传统方式一样运行两个 for/while 循环,但是如果我将在 两个数组中拥有超过 10k 个元素,这将非常慢。

所以也许可以用 LINQ 完成一些事情。

【问题讨论】:

  • 使用List<T>,而不是ArrayList
  • 你应该使用数据库。
  • 我同意@SLaks,使用外部选择为Select DistinctInner Select doing your Group By Having Count(*) >= 2 的子查询会更容易
  • “教科书”的实现是对两个序列进行排序(O(n lg n),如果需要比较排序),然后将这些序列放在一起。这是数据库使用的几种算法之一。
  • @user2864740:你的意思是O(n lg n)。或者制作字典并在O(n) 中进行操作。

标签: c# arrays linq arraylist duplicates


【解决方案1】:

如果你想使用 linQ,你需要编写你自己的 EqualityComparer 来覆盖 EqualsGetHashCode()

 public class ProductDetails
    { 
        public string id {get; set;}
        public string description {get; set;}
        public float rate {get; set;}
    }

public class ProductComparer : IEqualityComparer<ProductDetails>
{

    public bool Equals(ProductDetails x, ProductDetails y)
    {
        //Check whether the objects are the same object. 
        if (Object.ReferenceEquals(x, y)) return true;

        //Check whether the products' properties are equal. 
        return x != null && y != null && x.id.Equals(y.id) && x.description.Equals(y.description);
    }

    public int GetHashCode(ProductDetails obj)
    {
        //Get hash code for the description field if it is not null. 
        int hashProductDesc = obj.description == null ? 0 : obj.description.GetHashCode();

        //Get hash code for the idfield. 
        int hashProductId = obj.id.GetHashCode();

        //Calculate the hash code for the product. 
        return hashProductDesc ^ hashProductId ;
    }
}

现在,假设你有这个对象:

ProductDetails [] items1= { new ProductDetails { description= "aa", id= 9, rating=2.0f }, 
                       new ProductDetails { description= "b", id= 4, rating=2.0f} };

ProductDetails [] items= { new ProductDetails { description= "aa", id= 9, rating=1.0f }, 
                       new ProductDetails { description= "c", id= 12, rating=2.0f } };


IEnumerable<ProductDetails> duplicates =
    items1.Intersect(items2, new ProductComparer());

【讨论】:

  • 感谢您提供如此好的注释代码。我肯定会尝试这段代码,但说真的,我不了解GetHashCode 的东西。如果我的自定义类中有更多字段怎么办?我需要用相同的模式^吗?喜欢hasProductDesc ^ hasProductId ^ hasProductRate ^ hasProductMake?另外,如果我只想根据描述比较这两个不同的对象/数组列表,意味着其他字段可以不同。
  • 嗨,如果您有多个属性,我不会这样做,因为如果您最终对许多属性进行异或运算,它们可能会收敛到 0,从而导致大量冲突。因此,破坏了 Equals 方法。阅读此内容以获取更多信息。 GetHashCode override of object containing generic array
  • 在上面的代码中,我假设相等是由 id 和 description 定义的。如果仅通过描述,您可以删除此x.id.Equals(y.id) 和此^ hashProductId,它应该可以正常工作。
【解决方案2】:

考虑重写 System.Object.Equals 方法。

   public class ProductDetails
   {
     public string id;
     public string description;
     public float rate;

     public override bool Equals(object obj)
     {
       if(obj is ProductDetails == null)
          return false;

      if(ReferenceEquals(obj,this))
          return true;

       ProductDetails p = (ProductDetails)obj;
       return description == p.description;
    }
  }

过滤就这么简单:

var result = products1.Where(product=>products2.Contains(product));

编辑:

请考虑此实现不是最佳的..

此外,在 cmets 中针对您的问题提出了您使用数据库的建议。
这种方式将优化性能 - 根据数据库实现
无论如何 - 开销不会是你的。

但是,您可以使用 Dictionary 或 HashSet 优化此代码:
重载 System.Object.GetHashCode 方法:

public override int GetHashCode()
{
  return description.GetHashCode();
}

您现在可以这样做了:

var hashSet = new HashSet<ProductDetails>(products1);
var result = products2.Where(product=>hashSet.Contains(product));

这将在一定程度上提高您的性能,因为查找成本会更低。

【讨论】:

  • 您还必须覆盖GetHashCode()
  • 那是O(n^2)。使用字典。
  • @SLaks 抱歉,这个时间很懒。很快就会添加。
【解决方案3】:

10k 元素不算什么,但请确保使用正确的集合类型。 ArrayList 已被长期弃用,请使用 List&lt;ProductDetails&gt;

下一步是为您的班级实施正确的 EqualsGetHashCode 覆盖。这里的假设是 description 是关键,因为从重复的角度来看,这就是您所关心的:

public class ProductDetails
{
    public string id;
    public string description;
    public float rate;

    public override bool Equals(object obj)
    {
        var p = obj as ProductDetails;
        return ReferenceEquals(p, null) ? false : description == obj.description;
    }

    public override int GetHashCode() => description.GetHashCode();    
}

现在我们有了选择。一种简单有效的方法是使用哈希集:

var set = new HashSet<ProductDetails>();
var products1 = new List<ProductDetails>();  // fill it
var products2 = new List<ProductDetails>();  // fill it

// shove everything in the first list in the set
foreach(var item in products1)
    set.Add(item);

// and simply test the elements in the second set
foreach(var item in products2)
    if(set.Contains(item))
    {
        // item.description was already used in products1, handle it here
    }

这为您提供了线性 (O(n)) 时间复杂度,这是您能得到的最好的。

【讨论】:

    猜你喜欢
    • 2021-06-04
    • 2019-04-06
    • 1970-01-01
    • 2019-10-25
    • 2017-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多