2009-08-07 109 views

回答

213

重要的关于HashSet<T>是正确的,在名称:这是一个集。您可以使用单个套件做的唯一事情是确定其成员,并检查某个项目是否为成员。

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

这有点危险遍历一个HashSet<T>,因为这样做强加在集中的项目订单。该命令并不是该集合的一个属性。你不应该依赖它。如果订购集合中的物品对您来说很重要,那么这个集合不是集合。

集合非常有限且拥有独特的成员。另一方面,它们非常快。

+1

框架提供'SortedSet'数据结构的事实与您说的不是集合的属性相矛盾,或者指出开发团队的误解。 – Veverke 2016-08-03 16:07:36

+4

我认为说'HashSet'中的项的顺序没有定义是更正确的,所以不要依赖迭代器的顺序。如果你迭代集合,因为你正在对集合中的项目进行一些操作,那么*除非*依赖与订单有关的任何东西,否则*不是危险的*。 'SortedSet'具有'HashSet' * plus *顺序的所有属性,但'SortedSet'不是从'HashSet'派生的;换句话说,* SortedSet是不同对象的有序集合*。 – Kit 2016-09-15 21:38:27

+0

我很喜欢这个答案。但是,在呈现它时你显得很生气/沮丧/不高兴......这并不是我的忠实粉丝。 – pimbrouwers 2017-08-18 21:11:31

11

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

如果你想知道什么样的设置可能是有益的:显然,你想摆脱重复的地方。作为一个稍微有点人为的例子,假设您有一个软件项目的10.000版本的列表,并且您想知道有多少人为该项目做出了贡献。您可以使用Set<string>并遍历修订列表并将每个修订的作者添加到该集。迭代完成后,集合的大小就是您正在寻找的答案。

+0

但是Set不允许检索单个元素?像集合[45]? – 2009-08-07 23:35:56

+2

为此,您需要遍历集合中的成员。其他典型的操作是检查该集合是否包含元素或获取集合的大小。 – earl 2009-08-07 23:39:40

14

性能将是一个坏的理由选择列表上的HashSet。相反,更好地捕捉你的意图?如果顺序很重要,那么Set(或HashSet)就不存在了。如果允许重复,也是如此。但是当我们不关心订单时,有很多情况,我们宁愿不要重复 - 那就是当你想要一个集合时。

+16

'性能会是选择HashSet而不是List的坏理由:我只是不同意你。这就是说选择Dictionray而不是两个列表对性能没有帮助。看看[下面的文章](http://geekswithblogs.net/BlackRabbitCoder/archive/2011/02/03/c.net-little-wonders-the-useful-but-overlooked-sets.aspx) – 2011-02-26 06:17:26

+11

@奥斯卡:我没有说组合不会更快 - 我说这将是选择它们的糟糕基础。如果你试图表示一个有序的集合,那么一个集合根本就不能工作,而试图对其进行索引是错误的;如果你想要的收藏品没有订单,那么一套完美 - 而且速度很快。但重要的是第一个问题:你想表达什么? – 2011-02-26 15:49:22

+2

但想一想。如果你想继续检查给定的字符串是否是某个10,000个字符串集合的成员,在技术上,'string []。Contains'和'HashSet .Contains'同样表达你的意图;选择HashSet的原因是它会运行得更快。 – Casey 2015-07-09 16:45:01

4

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

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

如果您想知道使用哪个,请注意,使用List<T>,其中HashSet<T>是appropiate是不是最大的错误,虽然它可能会允许你在哪儿集合中的不良重复的项目问题。更重要的是,查找(项目检索)效率要高得多 - 理想的情况是O(1)(用于理想的桶装)而不是O(n)时间 - 这在很多情况下非常重要。

+1

将现有项目添加到集合不会引发异常。添加将简单地返回false。另外:技术上哈希查找是O(n),而不是O(1),除非你有一个完美的哈希函数。当然,在实践中,假设它是O(1),除非散列函数非常糟糕,否则你会放弃它。 – sepp2k 2009-08-07 23:45:42

+1

@ sepp2k:是的,所以它返回一个布尔值......重点是,它通知你。哈希查找是*最坏的情况* O(n)如果你是buckets是可怕的 - 它更接近O(1)一般。 – Noldorin 2009-08-08 00:33:39

4

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

顾名思义,HashedSet<T>是实现set semantics的数据结构。数据结构被优化以实现集合操作(​​即联合,差异,相交),这对于传统的列表实现来说无法高效完成。

因此,选择使用哪种数据类型实际上取决于您试图对您的应用程序做什么。如果您不在乎您的元素是如何在一个系列中订购的,并且只想要检查是否存在,请使用HashSet<T>。否则,请考虑使用List<T>或其他合适的数据结构。

+2

另一个警告:集通常只允许一个元素出现。 – 2009-08-07 23:39:33

6

哈希集合最常见的用法是查看它们是否包含某个元素,它接近于O(1)操作(假设一个足够强的散列函数),而不是检查列表包含是O(n)(以及它是O(log n)的排序集)。所以如果你做了很多检查,不管某个项目是否包含在某个列表中,hahssets可能会提高性能。如果你只是遍历它们,那么不会有太大的区别(遍历整个集合是O(n),与列表相同,并且在添加项目时,哈希集合有更多的开销)。

不,你不能索引集,这是没有意义的,无论如何,因为集是没有顺序的。如果你添加一些项目,设置会不记得哪一个是第一,和第二等

+0

如果您只遍历它们,那么与List相比,HashSet方法会增加相当多的内存使用量。 – SamuelWarren 2010-05-26 18:23:10

1

总之 - 任何时候,你都倾向于使用词典(或词典其中S是T的属性),那么你应该考虑一个HashSet(或HashSet的+对T这相当于S上实现IEquatable)

+5

除非你关心密钥,那么你应该使用字典。 – Hardwareguy 2010-10-19 20:56:30

94

这里的,我用一个HashSet<string>一个真实的例子:我的语法高亮显示的虚幻的文件

部分是一项新功能,highlights Doxygen-style comments。我需要能够判断@\命令是否有效,以确定是以灰色(有效)还是红色(无效)显示它。我有一个HashSet<string>所有有效的命令,所以无论何时我在词法分析器中使用@xxx标记,我都会使用validCommands.Contains(tokenText)作为我的O(1)有效性检查。我真的不关心任何东西,除了在设置有效命令命令存在。让我们看看我面对的替代方案:

  • 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,对于大型设备来说也不算快,但对于小型设备来说,这值得尝试。尽管我有几百件物品,所以我通过了这个。
+6

感谢一个真实世界的例子 – 2014-07-06 02:16:20

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已成立的语义,通过在内部散列表实现:

一个集合是不包含 重复元素,并且其元素 没有特定的顺序。

如果失去索引/位置/列表行为,HashSet获得了什么? (1)添加,O(1)通过索引检索,O(1)检索索引,O(1)通过索引检索,O(1)通过索引检索,O (n)查找/删除)。

HashSet的行为可以与使用Dictionary<TKey,TValue>进行比较,只需将键值添加/删除并忽略字典值本身。您会希望字典中的键不会有重复的值,这就是“设置”部分的要点。

6

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”};

相关问题