C# - 将集合与自身进行比较以查找重复项的最快方法

时间:2016-05-26 14:30:14

标签: c# collections duplicates

public class TestObject
{
    string TestValue { get; set; }
    bool IsDuplicate { get; set; }
}

List<TestObject> testList = new List<TestObject>
{
    new TestObject { TestValue = "Matt" },
    new TestObject { TestValue = "Bob" },
    new TestObject { TestValue = "Alice" },
    new TestObject { TestValue = "Matt" },
    new TestObject { TestValue = "Claire" },
    new TestObject { TestValue = "Matt" }
};

想象一下,testList实际上是数百万个物品。

确保将TestObjects匹配的TestValue中的两个IsDuplicate的{​​{1}}设为真的最快方法是什么?无论给定值的实例如何,只有一个应该在IsDuplicate为false的过程中出现。

我不反对通过线程来做这件事。如果将其转换为另一种集合类型更快,则该集合不必是列表。

我需要保留重复项并将其标记,不要将其从集合中删除。

要扩展,这是(正如您可能想到的)一个更复杂问题的简单表达。有问题的对象已经有了一个序数,我可以使用它来订购它们。

在精确字符串相等性上匹配初始重复项之后,我将不得不再次返回该集合并使用一些模糊匹配逻辑重新尝试其余部分。在重复数据删除期间或之后,在此过程开始时存在的集合不会被更改。

最终原始集合将被写入文件,可能会重复标记。

5 个答案:

答案 0 :(得分:11)

正如其他人提到的,这里正确的方法是使用HashSet类。

var hashSet = new HashSet<string>();

foreach (var obj in testList)
{
    if (!hashSet.Add(obj.TestValue))
    {
        obj.IsDuplicate = true;
    }
}

当您第一次向HashSet添加值时,它会成功添加,HashSet.Add()方法返回true,因此您不会对该项进行任何更改。当您第二次尝试添加时,HashSet.Add()会返回false并将您的商品标记为重复。

完成运行我们的标记重复方法后,列表将具有以下状态:

Matt
Bob
Alice
Claire
Matt DUPLICATE

答案 1 :(得分:2)

这可能非常有效:

foreach (var dupe in testList.GroupBy(x => x.TestValue).SelectMany(g => g.Skip(1)))
    dupe.IsDuplicate = true;

[编辑]这个方法大约是上面接受的答案速度的三分之一,所以应该使用一个。这个答案仅仅是学术上的兴趣。

答案 2 :(得分:1)

可能我会在构建TestValue集合时检查重复项,以避免在数百万个元素上循环两次。如果这种情况可行,那么我会使用Dictionary<string, List<TestValue>>

Dictionary<string, List<TestValue>> myList = new Dictionary<string, List<TestValue>>();
while(NotEndOfData())
{
     TestValue obj = GetTestValue();
     if(myList.ContainsKey(obj.Name))
     {
         obj.IsDuplicate = true;
         myList[obj.Name].Add(obj);
     }
     else
     {
         obj.IsDuplicate = false;
         myList.Add(obj.Name, new List<TestValue>() { obj};
     }
}

答案 3 :(得分:1)

.topActive

正如您在问题中所允许的那样,我将SortedSet<string> sorted = new SortedSet<string>(); for (int i = 0; i < testList.Count; i++) testList[i].IsDuplicate = !sorted.Add(testList[i].TestValue); 更改为数组而不是列表,以使索引器更快。

答案 4 :(得分:0)

由于您表示您拥有保留商品序数的属性。我们可以使用该属性在将项目标记为重复项后将排序顺序重置为原始。

以下代码是自我解释的。但是,如果您需要进一步解释,请告诉我。

我假设属性名称为SortOrder。相应地修改代码。

void MarkDuplicates()
{
    testList = testList.OrderBy(f => f.TestValue).ThenBy(f => f.SortOrder).ToList();
    for (int i = 1; i < testList.Count; i++) 
    {
        if (testList[i].TestValue == testList[i - 1].TestValue) testList[i].IsDuplicate = true;
    }
    testList = testList.OrderBy(f => f.SortOrder).ToList();
}

我不是表演专家。但您可以计算此处提供的各种解决方案并自行检查性能。