【问题标题】:Having trouble removing duplicates in List<class>无法删除 List<class> 中的重复项
【发布时间】:2014-06-27 18:55:01
【问题描述】:

我有一个班级和清单:

    public class className
    {
        public string firstParam { get; set; }
        public string secondParam { get; set; }
    }

    public static List<className> listName = new List<className>();

列表包括(例如):

Apple    Banana
Corn     Celery
Corn     Celery
Corn     Grapes
Raisins  Pork

我正在尝试编辑列表(或创建新列表)以获取:

Apple    Banana
Corn     Celery
Corn     Grapes
Raisins  Pork

我试过了:

var listNoDupes = listName.Distinct();

还有:

IEnumerable<className> listNoDupes = listName.Distinct();

但两者都以与以前相同的条件返回列表,但有重复项。

【问题讨论】:

  • CLR 如何知道 className 对象的“不同”对您意味着什么?
  • 你怎么知道用什么替换重复的?
  • 输入列表是否总是按照您的示例排序,还是完全随机排序?

标签: c# visual-studio-2010 list duplicates ienumerable


【解决方案1】:

您需要覆盖/实现Equals()GetHashCode(),现在您正在列出不同的实例,并且它们彼此完全不同/唯一。

【讨论】:

  • 哦,我明白了!我会调查的。
  • @user2340818 google.com/…
  • 让对象的标识成为该对象的可变属性并不是一个特别好的设计。
  • 那么他应该将他的属性设为只读,我的答案在技术上是正确的,也是最简单的答案。
  • @TMcKeown 这肯定不是最简单的。 Habib绝对简单。学习如何在庄园中实现复合对象的哈希码既正确又能产生合理分布的哈希,这并非易事。
【解决方案2】:

您遇到的问题是对象的身份不是您想的那样。你的直觉告诉你,身份是firstParamsecondParam 的组合。真正发生的是className 的每个不同实例都有自己的身份,不依赖于对象的实现。您将需要覆盖通过System.Object 提供的方法,主要是EqualsGetHashCode,尽管您可能不会覆盖GetHashCode(这将是哈希集正常工作所必需的。)

【讨论】:

    【解决方案3】:

    如果你的类只包含这两个字段,那么你也可以这样做:

    var listNoDupes = listName.GroupBy(r => new { r.firstParam, r.secondParam })
                            .Select(grp => grp.First())
                            .ToList();
    

    或者您可以回复IEnumerable&lt;T&gt;,例如:

    IEnumerable<className> listNoDupes = 
                           listName
                               .GroupBy(r => new { r.firstParam, r.secondParam })
                               .Select(grp => grp.First());
    

    上面的代码将在属性firstParamsecondParam 上进行分组,稍后grp.First 将返回该组中的一个项目,您最终将得到每个组中的一个项目,(没有重复)

    【讨论】:

    • 虽然你应该记得考虑数据集的大小。
    • @ChaosPandion 以什么方式?
    • 这是否优于 Equals 和 GetHashCode?我的课程实际上只包含这两个字段。
    • @ChaosPandion 它为列表中的每个项目创建一个额外的项目是的,但是非常短暂的对象具有非常低的开销。如果项目是长期存在的,或者集合的大小如此之大以至于它可以放入内存一次,但不能两次,那么它可能是一个问题,是的。不过,该列表需要非常 大,这样大多数人都不会遇到这种情况。
    • @ChaosPandion 虽然IGrouping 本身并不继承自IList,但GroupBy 的实际MS 实现返回一个确实 实现IList 的类型,所以您描述的优化实际执行的。
    【解决方案4】:

    还有第三种可能性——使用采用IEqualityComparerDistinct 方法版本。不幸的是,C# 不支持创建匿名的、临时的接口实现。我们可以创建辅助类和扩展:

    public static class IEnumerableExtensions
    {
        public class LambdaEqualityComparer<T> : IEqualityComparer<T>
        {
            private Func<T, T, bool> comparer;
            private Func<T, int> hash;
    
            public LambdaEqualityComparer(Func<T, T, bool> comparer, 
                                          Func<T, int> hash)
            {
                this.comparer = comparer;
                this.hash = hash;
            }
    
            public bool Equals(T x, T y)
            {
                return comparer(x, y);
            }
    
            public int GetHashCode(T x)
            {
                return hash(x);
            }
        }
    
        public static IEnumerable<T> Distinct<T>(this IEnumerable<T> elems,
                                                 Func<T, T, bool> comparer,
                                                 Func<T, int> hash)
        {
            return elems.Distinct(new LambdaEqualityComparer<T>(comparer, hash));
        }
    }
    

    然后我们可以为Distinct 方法提供lambda:

    var filteredList = myList.Distinct((x, y) => x.firstParam == y.firstParam &&
                                                 x.secondParam == y.secondParam,
                                        x => 17 * x.firstParam.GetHashCode() + x.secondParam.GetHashCode());
    

    这允许您在单次拍摄中区分对象,而无需实现 EqualsGetHashCode。例如,如果项目中有一个地方,您在其中调用 Distinct,这可能足以使用此扩展。另一方面,如果className 对象的标识是一个跨越许多方法和类的概念,那么肯定最好简单地定义EqualsGetHashCode

    【讨论】:

    • 17 * x.firstParam.GetHashCode() + x.secondParam.GetHashCode() 有时会在启用算术上溢/下溢检查时引发异常。好的,我想没有多少人会在 C# 中启用它,但它可能会发生。
    • @AndrewMorton 是的,由于这个原因,您编写的几乎每个 GetHashCode 实现都应该在 unchecked 块中,只是为了防止在未启用该设置的项目中使用,即使检查整个项目是相当罕见的。
    猜你喜欢
    • 1970-01-01
    • 2015-12-29
    • 2012-09-28
    • 2019-10-13
    • 1970-01-01
    • 2014-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多