繁体   English   中英

快速使用String.Contains与巨大的列表C#

[英]Fast way to use String.Contains with huge list C#

我有这样的事情:

List<string> listUser;
listUser.Add("user1");
listUser.Add("user2");
listUser.Add("userhacker");
listUser.Add("user1other");

List<string> key_blacklist;
key_blacklist.Add("hacker");
key_blacklist.Add("other");

foreach (string user in listUser)
{
   foreach (string key in key_blacklist)
   {
      if (user.Contains(key))
      {
           // remove it in listUser
      }
   }
}

listUser的结果是:user1,user2。

问题是如果我有一个巨大的listUser (超过1000万)和巨大的key_blacklist (100.000)。 那段代码非常慢。 反正有更快的速度吗?

更新:我在那里找到新的解决方案。 http://cc.davelozinski.com/c-sharp/fastest-way-to-check-if-a-string-occurs-within-a-string希望在他进入那里时帮助他人! :)

如果您无法控制构建用户列表的方式,则至少可以并行测试列表中的每个项目,这在具有多个核心的现代计算机上将加快检查的速度。

        listuser.AsParallel().Where(
            s =>
            {
                foreach (var key in key_blacklist)
                {
                    if (s.Contains(key))
                    {
                        return false; //Not to be included
                    }
                }

                return true; //To be included, as no match with the blacklist
            });

另外 - 你必须使用.Contains? .Equals会快得多,因为在几乎所有情况下,当HashCodes不同时,将确定不匹配,这只能通过整数比较来找到。 超级快。

如果你确实需要.Contains,你可能想考虑重组应用程序。 列表中的这些字符串真正代表什么? 单独的用户子组? 我是否可以在添加时测试每个字符串是否代表黑名单中的用户?

更新:回应下面的@Rawling的评论 - 如果你知道有一组有限的用户名,例如“hacker”作为子字符串,那么在运行每个用户名的.Equals测试之前,该集合必须非常大对候选人来说比跑步要慢。对候选人说。 这是因为HashCode非常快。

如果您使用实体框架或linq到sql,那么使用linq并将查询发送到服务器可以提高性能。 然后,而不是删除您实际查询满足要求的项目的项目,即名称不包含禁用表达式的用户:

listUser.Where(u => !key_blacklist.Any(u.Contains)).Select(u => u).ToList();

一种可能的解决方案是使用树状数据结构。

基本的想法是将列入黑名单的单词组织成这样:

+ h
| + ha
|   + hac
|     - hacker
|     - [other words beginning with hac]
| 
+ f
| + fu
|   + fuk
|     - fukoff
|     - [other words beginning with fuk]

然后,当您检查列入黑名单的单词时,如果您发现您的用户字符串甚至不包含“h”,则无法搜索以“hac”开头的单词列表。

在我提供的示例中,使用您的示例数据,这当然没有任何区别,但是对于真实数据集,这应该显着减少Contains的数量,因为您不会每次检查列入黑名单的完整列表。

这是一个代码示例(请注意代码非常糟糕,这只是为了说明我的想法)

using System;
using System.Collections.Generic;
using System.Linq;

class Program {

    class Blacklist {
        public string Start;
        public int Level;
        const int MaxLevel = 3;
        public Dictionary<string, Blacklist> SubBlacklists = new Dictionary<string, Blacklist>();
        public List<string> BlacklistedWords = new List<string>();

        public Blacklist() {
            Start = string.Empty;
            Level = 0;
        }

        Blacklist(string start, int level) {
            Start = start;
            Level = level;
        }

        public void AddBlacklistedWord(string word) {
            if (word.Length > Level && Level < MaxLevel) {
                string index = word.Substring(0, Level + 1);
                Blacklist sublist = null;
                if (!SubBlacklists.TryGetValue(index, out sublist)) {
                    sublist = new Blacklist(index, Level + 1);
                    SubBlacklists[index] = sublist;
                }
                sublist.AddBlacklistedWord(word);
            } else {
                BlacklistedWords.Add(word);
            }
        }

        public bool ContainsBlacklistedWord(string wordToCheck) {
            if (wordToCheck.Length > Level && Level < MaxLevel) {
                foreach (var sublist in SubBlacklists.Values) {
                    if (wordToCheck.Contains(sublist.Start)) {
                        return sublist.ContainsBlacklistedWord(wordToCheck);
                    }
                }
            }
            return BlacklistedWords.Any(x => wordToCheck.Contains(x));
        }

    }

    static void Main(string[] args) {

        List<string> listUser = new List<string>();
        listUser.Add("user1");
        listUser.Add("user2");
        listUser.Add("userhacker");
        listUser.Add("userfukoff1");

        Blacklist blacklist = new Blacklist();
        blacklist.AddBlacklistedWord("hacker");
        blacklist.AddBlacklistedWord("fukoff");

        foreach (string user in listUser) {
            if (blacklist.ContainsBlacklistedWord(user)) {
                Console.WriteLine("Contains blacklisted word: {0}", user);
            }
        }
    }
}

你使用的是错误的东西。 如果您有大量数据,则应使用HashSet<T>SortedSet<T> 如果您不需要对数据进行排序,请使用HashSet<T> 这是我写的一个程序来演示时差:

class Program
{
    private static readonly Random random = new Random((int)DateTime.Now.Ticks);

    static void Main(string[] args)
    {
        Console.WriteLine("Creating Lists...");
        var stringList = new List<string>();
        var hashList = new HashSet<string>();
        var sortedList = new SortedSet<string>();

        var searchWords1 = new string[3];
        int ndx = 0;

        for (int x = 0; x < 1000000; x++)
        {
            string str = RandomString(10);

            if (x == 5 || x == 500000 || x == 999999)
            {
                str = "Z" + str;
                searchWords1[ndx] = str;
                ndx++;
            }
            stringList.Add(str);
            hashList.Add(str);
            sortedList.Add(str);
        }

        Console.WriteLine("Lists created!");
        var sw = new Stopwatch();
        sw.Start();
        bool search1 = stringList.Contains(searchWords1[2]);
        sw.Stop();
        Console.WriteLine("List<T> {0} ==> {1}ms", search1, sw.ElapsedMilliseconds);
        sw.Reset();
        sw.Start();
        search1 = hashList.Contains(searchWords1[2]);
        sw.Stop();
        Console.WriteLine("HashSet<T> {0} ==> {1}ms", search1, sw.ElapsedMilliseconds);
        sw.Reset();
        sw.Start();
        search1 = sortedList.Contains(searchWords1[2]);
        sw.Stop();
        Console.WriteLine("SortedSet<T> {0} ==> {1}ms", search1, sw.ElapsedMilliseconds);
    }

    private static string RandomString(int size)
    {
        var builder = new StringBuilder();
        char ch;
        for (int i = 0; i < size; i++)
        {
            ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
            builder.Append(ch);
        }

        return builder.ToString();
    }
}

在我的机器上,我得到以下结果:

Creating Lists...
Lists created!
List<T> True ==> 15ms
HashSet<T> True ==> 0ms
SortedSet<T> True ==> 0ms

如您所见, List<T>HashSet<T>SortedSet<T>极慢的分区。 那几乎是瞬间完成的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM