什么是类似于';x%';在键上的c#集合中
我需要在数十万个键上快速搜索前缀什么是类似于';x%';在键上的c#集合中,c#,performance,dictionary,collections,trie,C#,Performance,Dictionary,Collections,Trie,我需要在数十万个键上快速搜索前缀“类似sql的”。我曾尝试使用SortedList、Dictionary和SortedDictionary进行性能测试,我非常喜欢: var dictionary = new Dictionary<string, object>(); // add a million random strings var results = dictionary.Where(x=>x.Key.StartsWith(prefix)); 结果: 如果排序很重要,请
“类似sql的”
。我曾尝试使用SortedList、Dictionary和SortedDictionary进行性能测试,我非常喜欢:
var dictionary = new Dictionary<string, object>();
// add a million random strings
var results = dictionary.Where(x=>x.Key.StartsWith(prefix));
结果:
如果排序很重要,请尝试使用
SortedList
而不是SortedDictionary
。它们都具有相同的功能,但实现方式不同<当您想枚举元素(并且可以按索引访问元素)时,代码> SodeTrime更快,如果有很多元素,并且“代码”> SortedDictionary > /代码>更快,则希望在集合的中间插入一个新的元素。
所以试试这个:
var sortedList = new SortedList<string, object>();
// populate list...
sortedList.Keys.Any(k => k.StartsWith(lookup));
var sortedList=new sortedList();
//填充列表。。。
sortedList.Keys.Any(k=>k.StartsWith(lookup));
如果您有一百万个元素,但不想在填充字典后对它们重新排序,则可以结合它们的优点:使用随机元素填充
SortedDictionary
,然后从中创建一个新的列表
或分类列表
。如果可以对键进行一次排序,然后重复使用它们来查找前缀,则可以使用二进制搜索来加快搜索速度
为了获得最大性能,我将使用两个数组,一个用于键,一个用于值,并使用重载Array.Sort()
对主数组和辅助数组进行排序
然后可以使用Array.BinarySearch()
搜索以给定前缀开头的最近键,并返回匹配的索引
当我尝试时,如果有一个或多个匹配的前缀,每次检查似乎只需要大约0.003ms
这里有一个可运行的控制台应用程序要演示(请记住在发布版本上计时):
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用系统诊断;
使用System.Linq;
名称空间演示
{
班级计划
{
公共静态void Main()
{
整数计数=1000000;
object obj=新对象();
var keys=新字符串[计数];
var值=新对象[计数];
对于(int i=0;i0)和&array[index-1].StartsWith(前缀,StringComparison.OrdinalIgnoreCase))
--指数;
while((indexs[rng.Next(s.Length)]).ToArray();
}
静态只读随机rng=新随机(12345);
}
}
所以,在做了一点测试之后,我发现使用BinarySearch的方法非常接近,唯一的缺点是必须从a到z对键进行排序。但是列表越大,速度就越慢,因此三元搜索是二进制pc架构中最快的搜索
方法:()
检查这个:所以,在您的例子中,最快的是
var results=dictionary.Where(x=>x.Key.StartsWith(prefix))代码>?@RenatZamaletdinov使用正则表达式不太可能在这种特殊情况下执行。您是创建一次列表并多次搜索它,还是经常更新它?@Sebastian506563它的标签很差,它实际上不是bool,而是与键一起存储的实际对象的IEnumerable。
dictionary.Any(k => k.Key.StartsWith(randomstring)) took : 80990 ms
trie.Retrieve(lookup) took : 115 ms
var sortedList = new SortedList<string, object>();
// populate list...
sortedList.Keys.Any(k => k.StartsWith(lookup));
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace Demo
{
class Program
{
public static void Main()
{
int count = 1000000;
object obj = new object();
var keys = new string[count];
var values = new object[count];
for (int i = 0; i < count; ++i)
{
keys[i] = randomString(5, 16);
values[i] = obj;
}
// Sort key array and value arrays in tandem to keep the relation between keys and values.
Array.Sort(keys, values);
// Now you can use StartsWith() to return the indices of strings in keys[]
// that start with a specific string. The indices can be used to look up the
// corresponding values in values[].
Console.WriteLine("Count of ZZ = " + StartsWith(keys, "ZZ").Count());
// Test a load of times with 1000 random prefixes.
var prefixes = new string[1000];
for (int i = 0; i < 1000; ++i)
prefixes[i] = randomString(1, 8);
var sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; ++i)
for (int j = 0; j < 1000; ++j)
StartsWith(keys, prefixes[j]).Any();
Console.WriteLine("1,000,000 checks took {0} for {1} ms each.", sw.Elapsed, sw.ElapsedMilliseconds/1000000.0);
}
public static IEnumerable<int> StartsWith(string[] array, string prefix)
{
int index = Array.BinarySearch(array, prefix);
if (index < 0)
index = ~index;
// We might have landed partway through a set of matches, so find the first match.
if (index < array.Length)
while ((index > 0) && array[index-1].StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
--index;
while ((index < array.Length) && array[index].StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
yield return index++;
}
static string randomString(int minLength, int maxLength)
{
int length = rng.Next(minLength, maxLength);
const string CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return new string(Enumerable.Repeat(CHARS, length)
.Select(s => s[rng.Next(s.Length)]).ToArray());
}
static readonly Random rng = new Random(12345);
}
}
public static int BinarySearchStartsWith(List<string> words, string prefix, int min, int max)
{
while (max >= min)
{
var mid = (min + max) / 2;
var comp = string.CompareOrdinal(words[mid].Substring(0, prefix.Length), prefix);
if (comp >= 0)
{
if (comp > 0)
max = mid - 1;
else
return mid;
}
else
min = mid + 1;
}
return -1;
}
var keysToList = dictionary.Keys.OrderBy(q => q).ToList();
sw = new Stopwatch();
sw.Start();
foreach (var lookup in lookups)
{
bool exist = BinarySearchStartsWith(keysToList, lookup, 0, keysToList.Count - 1)!= -1
}
sw.Stop();