使哈希集<;字符串>;不区分大小写

本文关键字:不区 大小写 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