我何时应该使用HashSet <t>类型?</t>

时间:2009-08-07 23:25:36

标签: c# .net data-structures hashset

我正在探索HashSet<T>类型,但我不明白它在集合中的位置。

可以用它来代替List<T>吗?我认为HashSet<T>的表现会更好,但我看不到个人对其元素的访问。

仅用于枚举吗?

11 个答案:

答案 0 :(得分:220)

HashSet<T>的重要之处就在于名称:它是。单个集合中唯一可以做的就是确定其成员是什么,并检查项目是否是成员。

询问是否可以检索单个元素(例如set[45])是误解了集合的概念。没有集合中的第45个元素。集合中的项目没有排序。集合{1,2,3}和{2,3,1}在各方面都是相同的,因为它们具有相同的成员资格,并且成员资格是最重要的。

迭代HashSet<T>有点危险,因为这样做会对集合中的项目施加顺序。该订单实际上不是该集合的属性。你不应该依赖它。如果对集合中的项目进行排序对您来说很重要,那么该集合就不是一个集合。

集合非常有限且具有唯一成员。另一方面,他们真的很快。

答案 1 :(得分:103)

以下是我使用HashSet<string>

的真实示例

UnrealScript文件的部分语法高亮显示是highlights Doxygen-style comments的新功能。我需要能够判断@\命令是否有效,以确定是以灰色(有效)还是红色(无效)显示。我有HashSet<string>个有效命令,所以每当我点击词法分析器中的@xxx标记时,我都会使用validCommands.Contains(tokenText)作为我的O(1)有效性检查。除了有效命令的 set 中的命令的存在之外,我真的不关心任何事情。让我们看看我面临的替代方案:

  • Dictionary<string, ?>:我使用什么类型的值?由于我将使用ContainsKey,因此该值毫无意义。注意:在.NET 3.0之前,这是O(1)查找的唯一选择 - 为3.0添加HashSet<T>并扩展为4.0实现ISet<T>
  • List<string>:如果我对列表进行排序,我可以使用BinarySearch,即O(log n)(没有看到上面提到的这个事实)。但是,由于我的有效命令列表是一个永不改变的固定列表,因此这将永远不会比简单...
  • 更合适
  • string[]Array.BinarySearch再次给出O(log n)性能。如果列表很短,这可能是表现最佳的选项。它的空间开销始终低于HashSetDictionaryList。即使使用BinarySearch,对于大型集合来说也不会更快,但对于小型集合,它值得尝试。我有几百件物品,所以我传了这个。

答案 2 :(得分:23)

HashSet<T>实现ICollection<T>接口:

public interface ICollection<T> : IEnumerable<T>, IEnumerable
{
    // Methods
    void Add(T item);
    void Clear();
    bool Contains(T item);
    void CopyTo(T[] array, int arrayIndex);
    bool Remove(T item);

    // Properties
   int Count { get; }
   bool IsReadOnly { get; }
}

List<T>实施IList<T>,扩展了ICollection<T>

public interface IList<T> : ICollection<T>
{
    // Methods
    int IndexOf(T item);
    void Insert(int index, T item);
    void RemoveAt(int index);

    // Properties
    T this[int index] { get; set; }
}

HashSet设置了语义,通过内部哈希表实现:

  

集合是一个包含no的集合   重复元素,以及其元素   没有特别的顺序。

如果HashSet失去索引/位置/列表行为,它会获得什么?

从HashSet添加和检索项总是由对象本身,而不是通过索引器,并且接近O(1)操作(List是O(1)add,O(1)通过索引检索,O( n)查找/删除)。

通过仅添加/删除键作为值,并且忽略字典值本身,可以将HashSet的行为与使用Dictionary<TKey,TValue>进行比较。您可能希望字典中的键不具有重复值,这就是“Set”部分的重点。

答案 3 :(得分:14)

在List上选择HashSet是一个糟糕的理由。相反,有什么更好地捕捉你的意图?如果顺序很重要,那么Set(或HashSet)就会出局。如果允许重复,同样。但是在很多情况下我们不关心秩序,而且我们宁愿没有重复 - 那就是你想要一套。

答案 4 :(得分:11)

HashSet是由散列实现的 set 。集合是不包含重复元素的值的集合。集合中的值通常也是无序的。所以不,一个集不能用来替换列表(除非你首先应该使用一个集合)。

如果你想知道一个集合可能有什么用处:你想要摆脱重复的地方,显然。作为一个有点人为的例子,假设您有一个包含10,000个软件项目修订版的列表,并且您想知道有多少人为该项目做出了贡献。您可以使用Set<string>并迭代修订列表,并将每个修订版的作者添加到集合中。一旦你完成迭代,集合的大小就是你要找的答案。

答案 5 :(得分:7)

HashSet将用于删除IEnumerble集合中的重复元素。例如,

List<string> duplicatedEnumrableStrings = new List<string> {"abc", "ghjr", "abc", "abc", "yre", "obm", "ghir", "qwrt", "abc", "vyeu"};
HashSet<string> uniqueStrings = new HashSet(duplicatedEnumrableStrings);

运行这些代码后,uniqueStrings保持{“abc”,“ghjr”,“yre”,“obm”,“qwrt”,“vyeu”};

答案 6 :(得分:6)

对于散列集最常见的用法可能是看它们是否包含某个元素,它接近于对它们的O(1)操作(假设具有足够强的散列函数),而不是检查包含的列表是O(n)(以及它是O(log n)的有序集合)。因此,如果您进行了大量检查,某个项目是否包含在某个列表中,则hahssets可能会提高性能。如果你只是迭代它们,那就没有太大区别了(迭代整个集合是O(n),与列表和hashsets相同,在添加项目时有更多的开销)。

不,你不能索引一个集合,无论如何都没有意义,因为集合没有被排序。如果你添加一些项目,那么集合将不会记住哪一个是第一个,哪个是等等。

答案 7 :(得分:4)

HashSet<T>是.NET框架中的一个数据结构,它能够将mathematical set表示为一个对象。在这种情况下,它使用哈希码(每个项的GetHashCode结果)来比较集合元素的相等性。

一个集合与列表的不同之处在于它只允许在其中包含一个相同元素。如果您尝试添加第二个相同的元素,HashSet<T>将返回false。实际上,元素的查找非常快(O(1)时间),因为内部数据结构只是一个哈希表。

如果您想知道使用哪个,请注意使用List<T> HashSet<T>是合适的并不是最大的错误,尽管它可能会导致您的收藏中有不受欢迎的重复项目的问题。更重要的是,查找(项目检索)效率更高 - 理想情况下O(1)(完美的分组)而非O(n)时间 - 这在许多情况下非常重要。

答案 8 :(得分:4)

List<T>用于存储有序的信息集。如果您知道列表元素的相对顺序,则可以在固定时间内访问它们。但是,要确定元素在列表中的位置或检查列表中是否存在元素,查找时间是线性的。另一方面,HashedSet<T>不保证存储数据的顺序,因此为其元素提供了恒定的访问时间。

顾名思义,HashedSet<T>是实现set semantics的数据结构。数据结构经过优化,可以实现集合操作(​​即Union,Difference,Intersect),而传统的List实现无法有效地完成这些操作。

因此,选择使用哪种数据类型实际上取决于您尝试对应用程序执行的操作。如果您不关心元素在集合中的排序方式,并且只想创建或检查是否存在,请使用HashSet<T>。否则,请考虑使用List<T>或其他合适的数据结构。

答案 9 :(得分:1)

简而言之 - 无论何时你想要使用一个Dictionary(或一个S是T属性的字典),你应该考虑一个HashSet(或HashSet +在T上实现IEquatable,等同于S)

答案 10 :(得分:0)

在基本的预期方案中,如果您要对两个集合进行比LINQ更复杂的设置操作,则应使用| store | newid | amount | total | | ----- | ----- | ------ | ----- | | 123 | 123 | 12.3 | 1 | | 456 | 123 | 45.6 | 2 | | 789 | adsf | 78.9 | 3 | | 321 | 123f | | | | 789 | 1654 | | | 。在大多数情况下,像HashSet<T>DistinctUnionIntersect这样的LINQ方法就足够了,但是有时您可能需要更细粒度的操作,并且Except提供了:

  • HashSet<T>
  • UnionWith
  • IntersectWith
  • ExceptWith
  • SymmetricExceptWith
  • Overlaps
  • IsSubsetOf
  • IsProperSubsetOf
  • IsSupersetOf
  • IsProperSubsetOf

LINQ和SetEquals“重叠”方法之间的另一个区别是LINQ总是返回新的HashSet<T>,而IEnumerable<T>方法修改了源集合。