[英]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.