【问题标题】:Remove all elements that have at least one duplicates completely out of a list in C#从 C# 的列表中完全删除所有至少有一个重复项的元素
【发布时间】:2019-02-03 06:09:16
【问题描述】:

简介

我是一名比利时软件工程师,在一家生产折弯机的公司工作。我现在有一个有趣的问题,我想知道最好的解决方案,性能在我的工作环境中真的很重要。我认为这对其他程序员来说可能也很有趣。

数据

  • 我有一个包含一堆“CS3DLine”类类型对象的列表。

    List <CS3DLine> ListParallelLines = new List<CS3DLine>();
    
  • 我还有一个自定义方法,它将其中两个对象作为参数,并返回一个布尔值,告诉这两个对象是否相等。

    public static bool IsSameLineIn3D(CS3DLine povleft, CS3DLine povright)
    

通缉

我想获得一个 FilteredListParallelLines,其中相等的 CS3DLines 完全 从列表中过滤掉。

备注

  • 在 Internet 上,我找到了使用 Distinct-method 和 IEqualityComparer 的示例(例如,在 dotNetPerls 上的此页面上),但在这些情况下,仅删除了重复项,而不是具有重复项的原件。
  • 我知道我也可以尝试迭代解决这个问题,但我担心如果列表包含大量对象,这会导致性能不佳。

【问题讨论】:

  • 您需要定义一个IEqualityComparer&lt;CS3DLine&gt;,它不仅实现了相等性测试,而且还为每个行实例实现了一致的哈希码。然后您将能够使用基于集合的方法来消除欺骗。
  • 不管你怎么做,从列表中删除项目对于多线程来说总是很困难的。在测量之前不要担心性能。
  • 也许是对多个属性和过滤器的 groupby 计数为 1?也许问题在于该列表已经包含大量对象。使用适当的 IEqualityComparer 并获取 hash 有不接受重复的数据结构。它会精简你的清单
  • 如果我理解正确,如果有两个或更多行的实例相等,它们将被 all 丢弃。对吗?
  • 确实,@spender,这是正确的。它们应该全部被丢弃。

标签: c# list filter


【解决方案1】:

如果我理解正确,以下是可能满足您要求的基于集合的方法。我不能保证性能。

如果列表的顺序不重要,可以简化。

在没有CS3DLine 的定义的情况下,我为我自己的 Line 类提供了一个示例。

与以往一样,当使用基于集合的方法时,线类最好是不可变的。

void Main()
{
    List<Line> lines = new List<Line>();
    var comparer = LineEqualityComparer.Instance;
    var filtered = lines
        .Select((line, idx) => new { line, idx })
        .GroupBy(x => x.line, comparer)
        .Where(g => g.Count() == 1)
        .SelectMany(g => g)
        .OrderBy(x => x.idx)
        .Select(x => x.line);
}

class Line
{
    public int X1 { get; }
    public int Y1 { get; }
    public int X2 { get; }
    public int Y2 { get; }
}

class LineEqualityComparer : IEqualityComparer<Line>
{
    public static IEqualityComparer<Line> Instance { get; } = new LineEqualityComparer();
    public bool Equals(Line x, Line y)
    {
        //fill-in the blanks
    }

    public int GetHashCode(Line obj)
    {
        //fill-in the blanks
    }
}

在大型数据集上,您可能可以通过在 linq 方法链中策略性地放置 .AsParallel()somewhere 来获得更好的查询性能。 p>

【讨论】:

  • 嗨@spender,我使用了这个解决方案,经过测试,我可以确认它可以正常工作。非常感谢。我省略了 OrderBy,因为在我的情况下排序并不重要。
【解决方案2】:

对于复杂的对象,你需要重写 Equals 和 GetHashCode 之后你可以简单地比较它

http://www.loganfranken.com/blog/687/overriding-equals-in-c-part-1/

【讨论】:

    【解决方案3】:

    由于您要求从列表中完全删除具有任何重复项的项目,一种方法是首先对您的集合进行分组,然后根据具有多个项目的任何组进行过滤。

    这种过滤的性能总是有限的,但为了节省分组时间,并且如果您让对象提前维护自己的哈希以进行分组,则必须运行相等比较,这将减少想要过滤时的负载并且哈希需要通过对给定实例的更改来持续更新。如果您的硬件是限制,则考虑因素会有所不同,因此您不希望将所有项目的哈希值存储在内存中,或者如果它是您关心的速度。存储哈希而不计算它们并不理想,因为代码中可能存在移动部分,可能会无意中触发哈希更新,但如果性能是一个重要因素,如果仔细实施可能会有所帮助。

    var results = ListParallelLines.GroupBy(x => x.EqualityHash).Where(x => x.Count() == 1);
    

    这会给你一个哈希返回一个没有重复的项目列表。

    GetHashCode() 有一个默认实现,但它很可能发生冲突,而且我过去曾见过一个问题,因为它引起了巨大的头痛,所以尽量避免使用它。

    https://docs.microsoft.com/en-us/dotnet/api/system.object.gethashcode?redirectedfrom=MSDN&view=netframework-4.7.2#remarks

    【讨论】:

    • 覆盖GetHashCode 和提供您自己的唯一命名属性(除了生成非标准代码)之间没有区别。仅使用哈希码来确定相等性是错误的。在两个项目的哈希匹配的情况下,任何哈希表都需要一个哈希码然后是一个相等方法。阅读有关冲突解决的 wikipedia 条目,以了解为什么需要哈希和相等。
    • 不使用按需生成的哈希值是为了在过滤列表时节省时间,因此提供一个始终保存值的属性,该值不需要在访问点计算考虑到对性能的关注,它确实适用于大型数据集。虽然不像数据的非规范化那样理想,但它是有目的的。碰撞不太可能(但仍然可能),选择正确的散列方法将有助于缓解这种情况。如果性能是重点,那么非常低的碰撞几率可能是可以接受的权衡。
    • 更进一步,为了完全避免这个问题,但仍然节省开销,您可以只对具有匹配哈希的那些进行相等检查,如果准确性至关重要,这将大大减少可能不需要的检查数量。如果性能不是问题的重点,我建议您完全按照您的做法实施 EqualityComparer。如果我错过了什么,我很想听听你的看法。
    【解决方案4】:

    在第一步中,您需要为您的CS3DLines 类创建一个实现IEqualityComparer 的类。

    这可能看起来很接近:

    public class CS3DComparer : IEqualityComparer {
        public bool Equals(CS3DLines a, CS3DLines b) {
            return IsSameLineIn3D(a, b);
        }
        public int GetHashCode(CS3DLines line){
            // You do not need to use all properties of line to calculate the 
            // hashCode. If performance is not good enough you can experiment by 
            // adding and removing properties from the hash code calculation.
    
            var hashCode = line.Property1?.GetHashCode() ?? 0;
            hashCode = (hashCode * 397) ^ (line.Property2?.GetHashCode() ?? 0);
            hashCode = (hashCode * 397) ^ (line.Property3?.GetHashCode() ?? 0);
            return hashCode;
        }
    }
    

    接下来要获取ListParallelLines 集合中所有元素的未排序列表,您可以调用以下代码:

    var singles = ListParallelLines
        .GroupBy(line => line, new CS3DComparer())
        .Where(group => group.Count() == 1)
        .Select(group => group.Key)
        .ToList();
    

    singles 现在是ListParallelLines 中没有重复的所有行的列表。

    为了通过并行化实现可能的加速,您可以尝试使用 PLINQ,方法是调用 AsParallel() 启动 LINQ 查询。

    var singles = ListParallelLines
        .AsParallel()
        .GroupBy(line => line, new CS3DComparer())
        .Where(group => group.Count() == 1)
        .Select(group => group.Key)
        .ToList();
    

    【讨论】:

      猜你喜欢
      • 2014-03-10
      • 1970-01-01
      • 1970-01-01
      • 2023-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多