我需要一个快速方法来确定给定字符串是否在字符串列表中。
直到运行时才知道字符串列表,但此后它不会改变。
我可以简单地使用名为List<String>
的{{1}},然后执行:
strings
但是如果列表中有许多字符串,这将表现不佳。
我也可以使用if (strings.Contains(item))
,但这需要在每个传入的字符串以及HashSet<String>
上调用GetHashCode
,如果存在例如{0},那将是一种浪费。列表中只有3个字符串。我是否提到这需要快速?
我可以在设置时,根据字符串的数量决定使用Equals
或List
(例如,使用List少于10个字符串,否则使用HashSet),而不是像HashSet
。
由于字符串是unicode,标准的Trie结构不起作用,尽管Radix树/ Patricia trie可能。那里有基准测试的C#实现吗?
有些人提到绕过HybridDictionary
的{{1}}并使用速度更快的哈希函数。那里有基准吗?
使用LINQ表达式实质上创建一个优化的switch语句是一种看起来非常有趣的新方法。
还有什么办法?设置成本并不重要,只是搜索速度。
如果重要,传入的字符串值很少会出现在列表中。
答案 0 :(得分:5)
您可以使用trie来保存字符串列表;尝试是为快速重新 trie val而设计的。这是在{C#中实现trie的one example。
更新:Powerpoint presentation on folded tries for Unicode和Ifo on implementation of a folded trie for Unicode (not C#)
答案 1 :(得分:3)
看看这些:
Jomo Fisher - Fast Switching with LINQ
Gustavo Guerra - StaticStringDictionary - Fast Switching with LINQ revisited
答案 2 :(得分:2)
您是否考虑过使用HashSet类(在.NET 3中)?
答案 3 :(得分:2)
关注你的“当名单很小”时;如果你不介意使用非泛型集合,System.Collections.Specialized.HybridDictionary
会做这样的事情;它在小时封装System.Collections.Specialized.ListDictionary
,或在变大时System.Collections.Hashtable
封装>10
。值得一看?
否则;您可以将HashSet<T>
与自定义比较器一起使用吗?然后你可以选择GetHashCode()
的价格是多少......
using System;
using System.Collections.Generic;
class CustomStringComparer : IEqualityComparer<string> {
public bool Equals(string x, string y) {
return string.Equals(x, y);
}
public int GetHashCode(string s) {
return string.IsNullOrEmpty(s) ? 0 :
s.Length + 273133 * (int)s[0];
}
private CustomStringComparer() { }
public static readonly CustomStringComparer Default
= new CustomStringComparer();
}
static class Program {
static void Main() {
HashSet<string> set = new HashSet<string>(
new string[] { "abc", "def", "ghi" }, CustomStringComparer.Default);
Console.WriteLine(set.Contains("abc"));
Console.WriteLine(set.Contains("abcde"));
}
}
答案 4 :(得分:2)
也许HybridDictionary是更好的选择。它的内部使用取决于集合中的项目数量。
答案 5 :(得分:2)
我最终这样做了:
private static bool Contains(List<string> list, string value)
{
bool contains = null != list.Find(str => str.ToLower().Equals(value.ToLower()));
return contains;
}
我猜你可以为List<string>
创建一个扩展方法,但这足以满足我的需求。
答案 6 :(得分:0)
顺便说一下,如果内存服务,当构造一个String时,它的HashValue会被预先计算并与String一起存储,作为这种用例的优化。如果您正在使用字符数组或StringBuilder,这显然不适用,但对于不可变的String,它应该适用。
编辑:我不正确...... Java会缓存String的HashCode,而C#不会。
答案 7 :(得分:0)
您可以使用字符串实习来快速完成此操作。
构建列表时,必须存储所需字符串的实习格式(string.Intern()
的结果)。然后,您需要使用object.ReferenceEquals
与实习字符串进行比较 - 因为实习字符串具有相同的引用。
List<string> BuildList() {
List<string> result;
foreach (string str from StringSource())
result.Add(str.Intern());
return result;
}
bool CheckList(List<string> list, string stringToFind) { // list must be interned for this to work!
return list.Find(str => object.ReferenceEquals(str, stringToFind)) != null;
}
这将导致每个列表进行四字节比较,并对原始字符串进行一次传递。实习字符串池专门用于快速字符串比较和查找是否已存在,因此实习操作应该非常快。