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