使HashSet <string>不区分大小写
我有HashSet参数的方法。 我需要做大小写不敏感包含在其中:
public void DoSomething(HashSet<string> set, string item) { var x = set.Contains(item); ... }
是否有任何方法使现有的HashSet不区分大小写(不要创build新的)?
我正在寻找最佳性能的解决scheme。
编辑
包含可以被多次调用。 所以IEnumerable扩展对我来说是不可接受的,因为性能比本地的HashSet Contains方法要低。
解
既然,回答我的问题是NO,这是不可能的,我创build并使用了下面的方法:
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"));
在构buildHashSet<T>的时候你必须做这个改变。 一旦存在,就不能更改它正在使用的IEqualityComparer<T> 。
只要你知道,默认情况下(如果你没有传入任何IEqualityComparer<T>到HashSet<T>构造函数),它就会使用EqualityComparer<T>.Default 。
编辑
这个问题似乎已经改变了,我发布我的答案。 如果您必须在现有区分大小写的 HashSet<string>执行不区分大小写的search,则必须执行线性search:
set.Any(s => string.Equals(s, item, StringComparison.OrdinalIgnoreCase));
这是没有办法的。
您不能神奇地使大小写敏感的HashSet(或字典)以不区分大小写的方式行事。
如果不能依赖传入的HashSet来区分大小写,则必须在函数内重新创build一个。
最紧凑的代码 – 使用现有的构造函数 :
var insensitive = new HashSet<string>( set, StringComparison.InvariantCultureIgnoreCase);
请注意,复制HashSet和遍历所有项目一样昂贵,所以如果你的函数只是search,那么遍历所有项目会更便宜(O(n))。 如果您的函数多次调用来进行单个不区分大小写的search,则应该尝试将适当的HashSet传递给它。
HashSet被devise成根据其散列函数和等式比较器来快速find元素。 你所要求的是真的find一个匹配“其他”条件的元素。 想象一下,你有一个Set<Person>对象只使用Person.Name来进行比较,你需要find一个Person.Age给定值的Person.Age 。
重点是你需要迭代集合的内容来find匹配的元素。 如果你经常这样做,你可能会创build一个不同的Set,在你的情况下使用不区分大小写的比较器,但是你必须确保这个阴影集与原始的同步。
到目前为止的答案基本上是上述的变化,我想补充说明这个基本问题。
假设你有这个扩展方法:
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的构造函数可以采用另一种IEqualityComparer ,它可以覆盖如何确定相等性。 请看这里的构造函数列表。
StringComparer类包含一串用于string的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));