什么是类似于';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();