如何快速计算字符串在字符串列表的给定部分中出现的频率?
本文关键字:字符串 频率 计算 何快速 列表 定部 | 更新日期: 2023-09-27 18:14:34
我有一个字符串列表,我需要计算其中出现特定字符串的列表条目的数量(并且整个列表仅用于列表的子集而不是整个列表)。
下面的代码工作得很好,但是它的性能是.....遗憾的是,这不是一个可接受的niveau,因为我需要解析500k到900k个列表条目。对于这些条目,我需要运行下面的代码大约10k次(因为我需要分析列表的10k部分)。这需要177秒甚至更长时间。所以我的问题是,我如何才能做到这一点……快?
private int ExtraktNumbers(List<string> myList, int start, int end)
{
return myList.Where((x, index) => index >= start && index <= end
&& x.Contains("MYNUMBER:")).Count();
}
现在我们知道你调用了这个方法10,000次,这是我的建议。我假设你已经硬编码了"号码:",这意味着你在每个电话都做不同的范围?如果是这样的话…
首先,运行一个'indexing'方法并创建一个索引匹配的列表。然后你可以很容易地计算出你需要的匹配范围。
注意:这是一些快速的,你甚至可以进一步优化它:
List<int> matchIndex = new List<int>();
void RunIndex(List<string> myList)
{
for(int i = 0; i < myList.Count; i++)
{
if(myList[i].Contains("MYNUMBER:"))
{
matchIndex.Add(i);
}
}
}
int CountForRange(int start, int end)
{
return matchIndex.Count(x => x >= start && x <= end);
}
然后你可以这样使用,例如:
RunIndex(myList);
// I don't know what code you have here, this is just basic example.
for(int i = 0; i <= 10,000; i++)
{
int count = CountForRange(startOfRange, endOfRange);
// Do something with count.
}
另外,如果你检查的范围有很多重复,那么你可以考虑在字典中缓存范围计数,但在这个阶段很难判断这样做是否值得。
我很确定一个简单的迭代解决方案会表现得更好:
private int ExtractNumbers(List<string> myList, int start, int end)
{
int count = 0;
for (int i = start; i <= end; i++)
{
if (myList[i].Contains("MYNUMBER:"))
{
count++;
}
}
return count;
}
对于我的试验台 1000万 (比多10倍)行
var data = Enumerable
.Range(1, 10000000)
.Select(item => "123456789 bla-bla-bla " + "MYNUMBER:" + item.ToString())
.ToList();
Stopwatch sw = new Stopwatch();
sw.Start();
int result = ExtraktNumbers(data, 0, 10000000);
sw.Stop();
我得到了这些结果:
2.78秒-你的初始实现
初始循环(2.60秒):
private int ExtraktNumbers(List<string> myList, int start, int end) {
int result = 0;
for (int i = start; i < end; ++i)
if (myList[i].Contains("MYNUMBER:"))
result += 1;
return result;
}
PLinq (1.72 seconds):
private int ExtraktNumbers(List<string> myList, int start, int end) {
return myList
.AsParallel() // <- Do it in parallel
.Skip(start - 1)
.Take(end - start)
.Where(x => x.Contains("MYNUMBER:"))
.Count();
}
显式并行实现(1.66秒):
private int ExtraktNumbers(List<string> myList, int start, int end) {
long result = 0;
Parallel.For(start, end, (i) => {
if (myList[i].Contains("MYNUMBER:"))
Interlocked.Increment(ref result);
});
return (int) result;
}
我只是无法复制你的177秒
如果你从一开始就知道你想要考虑的间隔,那么循环一次列表可能是个好主意,就像上面的Dmytro和musefan建议的那样,所以我不会再重复同样的想法了。
然而,我有一个不同的建议,以提高性能。你如何创建你的清单?你事先知道项目的数量吗?因为对于如此大的列表,您可以通过使用List<T>
构造函数来获得初始容量,从而显著提高性能。