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

C# 在C中查找列表中重复项的最快方法#,c#,list,linq,duplicates,hashset,C#,List,Linq,Duplicates,Hashset,我知道在这个问题上有很多类似的问题,但是我找不到我想要的答案。这是我的要求 我有一个很长的字符串列表(很容易超过50000甚至100K个项目),我需要在其中找到重复的项目。但仅仅是找到重复品是不行的;我真正想做的是遍历列表,并在每个项目的末尾添加一个增量索引,以指示项目重复的次数。为了更好地说明,让我举一个例子。我的列表实际上包含路径,因此示例大致类似于此 我原来的名单: AAA\BBB AAA\CCC AAA\CCC BBB\XXX BBB BBB\XXX BBB\XXX 我添加了索引的调整

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

我有一个很长的字符串列表(很容易超过50000甚至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 + "]");
    }
}
List originalList=new List();
列表重复项=新列表();
//pathList是一个包含我的路径的简单列表。
foreach(路径列表中的字符串项)
{
//在这里做一些事情,只有当“项目”符合某些标准时才选择它。
如果(有效(项目))
{
原始列表。添加(项目);
int occurrences=originalList.Where(x=>x.Equals(item)).Count();
重复项。添加(项+“[”+事件+“]”);
}
}
这工作得很好,给了我想要的结果。问题是,考虑到我的列表可以包含10万个项目,它的速度非常慢。所以我环顾四周,发现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 + "[" + ??? + "]");
        }
    }
}
HashSet originalList=new HashSet();
列表重复项=新列表();
foreach(路径列表中的字符串项)
{
//在这里做一些事情,只有当“项目”符合某些标准时才选择它。
如果(有效(项目))
{
如果(!原始列表添加(项目))
{
重复项。添加(项+“[”+???+“]);
}
}
}
稍后,我可以将“[1]”添加到哈希集中的所有项中,但在将项添加到重复列表中时,如何正确获取索引(以上面的通用混淆符号标记)?我不能保留一个可以传递给我的方法的引用int,因为可能有数百个不同的重复项,每个重复项的重复次数与我的示例中的不同


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

您可以试试这个,尽管我还没有对它进行性能测试:

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++));
    }
}
List originalList=新列表()
{
@“AAA\BBB”,
@“AAA\CCC”,
@“AAA\CCC”,
@“BBB\XXX”,
@“BBB”,
@“BBB\XXX”,
@“BBB\XXX”
};
List outputList=新列表();
foreach(originalList.GroupBy中的变量g(x=>x).Select(x=>x.ToList())
{   
var指数=1;
foreach(g中的var项目)
{
Add(string.Format(“{0}[{1}]”,item,index++);
}
}
Fiddle

您可以使用Group()将字符串拉到一起,然后使用value和count组合来投影这些组

根据您的字符串列表:

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

既然你要求最快的速度,最好的IMO就是使用
foreach
循环和计数
字典
。它与
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 + "]");
    }
}
var counts=新字典(pathList.Count);//指定最大容量以避免重新灰化
foreach(路径列表中的字符串项)
{
//在这里做一些事情,只有当“项目”符合某些标准时才选择它。
如果(有效(项目))
{
整数计数;
计数。TryGetValue(项目,超出计数);
计数[项目]=++计数;
重复项。添加(项+“[”+计数+“]”);
}
}
这个怎么样

    static IEnumerable<string> MyCounter(IEnumerable<string> data)
    {
        var myDic = new Dictionary<string, int>();
        foreach (var d in data)
        {
            if (!myDic.ContainsKey(d))
                myDic[d] = 1;
            else
                myDic[d] = myDic[d] + 1 ;
            yield return d +"[" + myDic[d] + "]";
        }
    }
静态IEnumerable MyCounter(IEnumerable数据)
{
var myDic=新字典();
foreach(数据中的var d)
{
如果(!myDic.ContainsKey(d))
myDic[d]=1;
其他的
myDic[d]=myDic[d]+1;
收益率回报率d+“[”+myDic[d]+“]”;
}
}

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

private int GetCount(IDictionary<string, int> counts, string item)
{
  int count;
  if (!counts.TryGetValue(item, out count))
    count = 0;
  count++;
  counts[item] = count;
  return count;
}

private IEnumerable<string> GetItems(IEnumerable<string> items)
{
  // Initialize dict for counts with appropriate comparison
  var counts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
  foreach(var item in items)
    yield return string.Format("{0}[{1}]", item, GetCount(counts, item));
}
private int GetCount(IDictionary计数,字符串项)
{
整数计数;
如果(!counts.TryGetValue(项目,超出计数))
计数=0;
计数++;
计数[项目]=计数;
返回计数;
}
私有IEnumerable GetItems(IEnumerable items)
{
//通过适当的比较初始化计数的dict
var counts=新字典(StringComparer.OrdinalIgnoreCase);
foreach(项目中的var项目)
返回字符串。格式(“{0}[{1}]”,item,GetCount(counts,item));
}

您可以使用以下简洁易懂的代码:

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));
}
publicstaticvoidmain()
{
var originalList=新列表()
{
@“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));
}

是否希望在最后将它们全部粉碎到一个列表中?最好是这样。但是,如果它不是太慢而不是太多的列表,我也会考虑其他的选择。结果列表中元素的顺序是否重要?您不能使用<代码> HasSt/<代码>为您的原始列表,因为<代码> HasSET/<代码>不存储重复。这看起来更接近我的预期。让我试试,然后再报告。是的,我确实需要递增计数。我确实尝试过@maccettura的解决方案,效果很好;76131项的列表在1秒之内完成,而Linq的时间太长了。很好。考虑到需求,我喜欢这个实现。我在116个列表中运行了maccettura、Ivan Stoev和Markus的三个答案,每个列表10次。平均值以毫秒计如下:maccettura=13819,Ivan Stoev=13809,Markus=12966。