在C#中查找列表中重复项的最快方法

时间:2017-07-13 19:16:38

标签: c# list linq duplicates hashset

我知道在这个问题上有很多类似的问题,但我找不到我想要的答案。这是我的要求。

我有很长的字符串列表(很容易超过50,000或甚至100K项目),我需要在其中找到重复的项目。但只是发现重复不会做;我真正想要做的是浏览列表并在每个项目的末尾添加一个增量索引,以指示项目重复的次数。为了更好地说明,让我举一个例子。我的列表实际上包含路径,因此示例大致类似于。

我原来的名单:

AAA\BBB
AAA\CCC
AAA\CCC
BBB\XXX
BBB
BBB\XXX
BBB\XXX

我的调整后的列表添加了索引:

AAA\BBB[1]
AAA\CCC[1]
AAA\CCC[2]
BBB\XXX[1]
BBB[1]
BBB\XXX[2]
BBB\XXX[3]

首先,我使用Linq尝试了以下方法:

List<string> originalList = new List<string>();
List<string> duplicateItems = new List<string>();

// pathList is a simple List<string> that contains my paths.
foreach (string item in pathList)
{
    // Do some stuff here and pick 'item' only if it fits some criteria.
    if (IsValid(item))
    {
        originalList.Add(item);
        int occurences = originalList.Where(x => x.Equals(item)).Count();
        duplicateItems.Add(item + "[" + occurences + "]");
    }
}

这很好用,给了我想要的结果。问题是,由于我的列表可以包含100K项目,因此速度很慢。所以我环顾四周,了解到HashSet可能是一种可能更有效的替代品。但我无法弄清楚如何使用它来获得我想要的结果。

我可以尝试这样的事情,我想:

HashSet<string> originalList = new HashSet<string>();
List<string> duplicateItems = new List<string>();

foreach (string item in pathList)
{
    // Do some stuff here and pick 'item' only if it fits some criteria.
    if (IsValid(item))
    {
        if (!originalList.Add(item))
        {
            duplicateItems.Add(item + "[" + ??? + "]");
        }
    }
}

后来我可以添加&#34; [1]&#34;对于HashSet中的所有项目,但是如何在将项目添加到重复列表时将索引设置为正确(由混淆的通用符号,上面标记为???)?我不能保留一个我可以传递给我的方法的引用int,因为可能有数百个不同的重复项,每个重复项都重复不同的次数。

我仍然可以使用HashSet,还是有更好的方法来实现我的目标?即使是朝着正确方向的微小指针也会有很大的帮助。

6 个答案:

答案 0 :(得分:9)

由于您要求最快,最好的IMO将使用foreach循环并计算Dictionary<string, int>。它具有与HashSet相同的时间复杂度,并且使用的内存比LINQ GroupBy少得多:

var counts = new Dictionary<string, int>(pathList.Count); // specify max capacity to avoid rehashing
foreach (string item in pathList)
{
    // Do some stuff here and pick 'item' only if it fits some criteria.
    if (IsValid(item))
    {
        int count;
        counts.TryGetValue(item, out count);
        counts[item] = ++count;
        duplicateItems.Add(item + "[" + count + "]");
    }
}

答案 1 :(得分:3)

你可以试试这个,虽然我还没有进行过性能测试:

List<string> originalList = new List<string>()
{
    @"AAA\BBB",
    @"AAA\CCC",
    @"AAA\CCC",
    @"BBB\XXX",
    @"BBB",
    @"BBB\XXX",
    @"BBB\XXX"
};
List<string> outputList = new List<string>();

foreach(var g in originalList.GroupBy(x => x).Select(x => x.ToList()))
{   
    var index = 1;  
    foreach(var item in g)
    {
        outputList.Add(string.Format("{0}[{1}]", item, index++));
    }
}

小提琴here

答案 2 :(得分:1)

这个怎么样?

{{1}}

答案 3 :(得分:1)

您可以遍历列表并使用字典来获取计数,如下所示:

C:\dlib-19.4\dlib-19.4\dlib

答案 4 :(得分:1)

你可以使用这个清脆而脆脆的代码:

public static void Main()
{
    var originalList  = new List<string>()
    {
        @"AAA\BBB",
        @"AAA\CCC",
        @"AAA\CCC",
        @"BBB\XXX",
        @"BBB",
        @"BBB\XXX",
        @"BBB\XXX"
    };

    var outputList = originalList.GroupBy(x => x).SelectMany(x => x.Select((y, i) => string.Format("{0}[{1}]", y, i + 1)));     

    Console.WriteLine(string.Join("\n", outputList));
}

答案 5 :(得分:0)

您可以使用Group()将字符串拉到一起,然后使用值和计数的组合来投影这些组。

给出你的字符串列表:

var listOfStrings;
var grouped = listOfStrings.GroupBy(x => x);
var groupedCount = grouped.Select(x => new {key = x.Key, count = group.Count()});