簡體   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