88

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

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

有什么方法可以使现有的 HashSet 不区分大小写(不要创建新的)?

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

编辑

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

解决方案

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

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

7 回答 7

153

构造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


编辑

在我发布答案后,问题似乎发生了变化。如果您必须在现有 casesensitive 中进行不区分大小写的搜索必须进行线性搜索: HashSet<string>

set.Any(s => string.Equals(s, item, StringComparison.OrdinalIgnoreCase));

没有办法解决这个问题。

于 2013-06-04T16:04:00.723 回答
9

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

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

最紧凑的代码 - 使用现有集合中的构造函数:

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

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

于 2013-06-04T16:14:25.307 回答
4

HashSet旨在根据其散列函数和相等比较器快速查找元素。你所要求的实际上是找到一个匹配“其他”条件的元素。想象一下,您有一个Set<Person>Person.Name用于比较的对象,并且您需要找到一个给定值为 的元素Person.Age

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

到目前为止的答案基本上是上述的变体,我想补充一下以澄清基本问题。

于 2013-06-04T16:21:41.687 回答
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
于 2013-06-04T16:03:41.973 回答
2

的构造函数HashSet可以采用替代方法IEqualityComparer来覆盖如何确定相等性。请参阅此处的构造函数列表。

该类StringComparer包含一堆IEqualityComparersfor 字符串的静态实例。特别是,您可能对StringComparer.OrdinalIgnoreCase. StringComparer.

请注意,另一个构造函数接受一个IEnumerable,因此您可以HashSet从旧的构造一个新的,但使用IEqualityComparer.

所以,总而言之,您希望将您的转换HashSet如下:

var myNewHashSet = new HashSet(myOldHashSet, StringComparer.OrdinalIgnoreCase);
于 2013-06-04T16:03:51.503 回答
0

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

var contains = set.Any(a => a.Equals(item, StringComparison.InvariantCultureIgnoreCase));
于 2013-06-04T16:03:46.850 回答
-2

您现在可以使用

set.Contains(item, StringComparer.OrdinalIgnoreCase);

无需重新创建 HashSet

于 2019-08-29T12:30:45.023 回答