使HashSet <string>不区分大小写</string>

时间:2013-06-04 16:01:18

标签: c# .net hashset

我有HashSet参数的方法。我需要在其中包含不区分大小写的包含内容:

public void DoSomething(HashSet<string> set, string item)
{
    var x = set.Contains(item);
    ... 
}

是否可以使现有的HashSet不区分大小写(不创建新的)?

我正在寻找性能最佳的解决方案。

修改

可以多次调用包含。所以IEnumerable扩展对我来说是不可接受的,因为性能低于本机HashSet Contains方法。

解决方案

因为,回答我的问题是NO,这是不可能的,我已经创建并使用了以下方法:

public HashSet<string> EnsureCaseInsensitive(HashSet<string> set)
{
    return set.Comparer == StringComparer.OrdinalIgnoreCase
           ? set
           : new HashSet<string>(set, StringComparer.OrdinalIgnoreCase);
}

7 个答案:

答案 0 :(得分:98)

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));

没有办法解决这个问题。

答案 1 :(得分:6)

你不能神奇地使区分大小写的HashSet(或Dictionary)以不区分大小写的方式运行。

如果您不能依赖传入的HashSet不区分大小写,则必须在函数内重新创建一个。

大多数紧凑代码 - 使用现有集合中的constructor

var insensitive = new HashSet<string>(
   set, StringComparer.InvariantCultureIgnoreCase);

请注意,复制HashSet与遍历所有项目一样昂贵,因此,如果您的功能仅在搜索时执行,则迭代所有项目会更便宜(O(n))。如果您的函数多次调用以进行单个区分大小写的搜索,则应尝试将相应的HashSet传递给它。

答案 2 :(得分:4)

HashSet旨在根据其散列函数和相等比较器快速查找元素。你要求的是真正找到一个匹配“其他”条件的元素。想象一下,您有Set<Person>个对象仅使用Person.Name进行比较,您需要找到一个具有某个给定值Person.Age的元素。

关键是你需要迭代集合的内容来找到匹配的元素。如果您要经常这样做,您可能会创建一个不同的Set,在您使用不区分大小写的比较器的情况下,然后您必须确保此阴影集与原始同步。

到目前为止,答案基本上是上述的变化,我想补充一点来澄清根本问题。

答案 3 :(得分:3)

假设您有这种扩展方法:

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

答案 4 :(得分:2)

HashSet的构造函数可以使用替代IEqualityComparer,它可以覆盖如何确定相等性。请参阅构造函数列表here

StringComparer包含一堆用于字符串的IEqualityComparers静态实例。特别是,您可能对StringComparer.OrdinalIgnoreCase感兴趣。 HereStringComparer的文档。

请注意,另一个构造函数会引入IEnumerable,因此您可以使用HashSet构建旧版IEqualityComparer

所以,总而言之,您希望按以下方式转换HashSet

var myNewHashSet = new HashSet(myOldHashSet, StringComparer.OrdinalIgnoreCase);

答案 5 :(得分:0)

如果您想保留原始的区分大小写的版本,您可以使用不区分大小写的linq查询它:

var contains = set.Any(a => a.Equals(item, StringComparison.InvariantCultureIgnoreCase));

答案 6 :(得分:-1)

您现在可以使用

var path= Path.Combine(System.AppContext.BaseDirectory, "TextFileName.txt")
if (File.Exists(path)) 
{
   Console.WriteLine("The file exists.");
}

无需重新创建您的HashSet