我有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);
}
答案 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
感兴趣。 Here是StringComparer
的文档。
请注意,另一个构造函数会引入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