使哈希集<;字符串>;不区分大小写
本文关键字:不区 大小写 gt 字符串 lt 哈希集 | 更新日期: 2023-09-27 18:16:57
我有一个带有HashSet参数的方法。我需要做不区分大小写的包含:
public void DoSomething(HashSet<string> set, string item)
{
var x = set.Contains(item);
...
}
有没有任何方法可以使现有的HashSet不区分大小写(不要创建新的(?
我正在寻找性能最好的解决方案。
编辑
可以多次调用Contains。所以IEnumerable扩展对我来说是不可接受的,因为它的性能低于本机HashSet Contains方法。
解决方案
由于,我的问题的答案是否定的,这是不可能的,我创建并使用了以下方法:
public HashSet<string> EnsureCaseInsensitive(HashSet<string> set)
{
return set.Comparer == StringComparer.OrdinalIgnoreCase
? set
: new HashSet<string>(set, StringComparer.OrdinalIgnoreCase);
}
HashSet<T>
构造函数有一个重载,允许您传入自定义IEqualityComparer<string>
。静态StringComparer
类中已经为您定义了其中一些,其中一些忽略了大小写。例如:
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
set.Add("john");
Debug.Assert(set.Contains("JohN"));
您必须在构建HashSet<T>
时进行此更改。一旦存在,就不能更改它正在使用的IEqualityComparer<T>
。
正如您所知,默认情况下(如果您没有将任何IEqualityComparer<T>
传递给HashSet<T>
构造函数(,它将使用EqualityComparer<T>.Default
。
编辑
在我公布答案后,问题似乎发生了变化。如果您必须在现有的区分大小写的HashSet<string>
中进行不区分大小写的搜索,则必须进行线性搜索:
set.Any(s => string.Equals(s, item, StringComparison.OrdinalIgnoreCase));
这是没有办法的。
您不能神奇地使区分大小写的HashSet(或Dictionary(以不区分大小写方式进行操作。
如果不能依赖传入的HashSet
来区分大小写,则必须在函数中重新创建一个。
最紧凑的代码-使用现有集合中的构造函数:
var insensitive = new HashSet<string>(
set, StringComparer.InvariantCultureIgnoreCase);
请注意,复制HashSet
与遍历所有项目一样昂贵,因此如果您的函数只进行搜索,则遍历所有项目会更便宜(O(n((。如果您的函数多次调用以进行不区分大小写的搜索,则应该尝试将正确的HashSet
传递给它。
假设您有这个扩展方法:
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
return new HashSet<T>(source);
}
你可以使用这个:
set = set.Select(n => n.ToLowerInvariant()).ToHashSet();
或者,你可以这样做:
set = new HashSet(set, StringComparer.OrdinalIgnoreCase);
//or InvariantCultureIgnoreCase or CurrentCultureIgnoreCase
HashSet
设计用于根据其哈希函数和相等比较器快速查找元素。你真正想要的是找到一个符合"其他"条件的元素。假设您有一个仅使用Person.Name
进行比较的Set<Person>
对象,并且您需要找到一个具有给定值Person.Age
的元素。
重点是您需要对集合的内容进行迭代,以找到匹配的元素。如果你经常这样做,你可能会创建一个不同的集,在你的情况下使用不区分大小写的比较器,但你必须确保这个阴影集与原始集同步。
到目前为止的答案基本上是上述内容的变体,我想补充这一点以澄清根本问题。
HashSet
的构造函数可以采用一个替代IEqualityComparer
,它可以覆盖如何确定相等。请参阅此处的构造函数列表。
类StringComparer
包含用于字符串的IEqualityComparers
的一组静态实例。特别是,您可能对StringComparer.OrdinalIgnoreCase
感兴趣。这是StringComparer
的文档。
请注意,另一个构造函数接受了一个IEnumerable
,所以您可以从旧的构造函数构造一个新的HashSet
,但要使用IEqualityComparer
。
因此,总的来说,你想将你的HashSet
转换如下:
var myNewHashSet = new HashSet(myOldHashSet, StringComparer.OrdinalIgnoreCase);
如果你想保留原来的区分大小写的版本,你可以用不区分大小写:的linq查询它
var contains = set.Any(a => a.Equals(item, StringComparison.InvariantCultureIgnoreCase));
您现在可以使用
set.Contains(item, StringComparer.OrdinalIgnoreCase);
无需重新创建HashSet