【问题标题】:What is the difference between HashSet<T> and List<T>?HashSet<T> 和 List<T> 有什么区别?
【发布时间】:2011-06-17 20:56:38
【问题描述】:

您能解释一下.NET 中HashSet&lt;T&gt;List&lt;T&gt; 之间的区别吗?

也许您可以举例说明在哪些情况下HashSet&lt;T&gt; 应该优于List&lt;T&gt;

【问题讨论】:

标签: .net list hashset difference


【解决方案1】:

不同于列表 ...

  1. HashSet 是一个没有重复成员的 List。

  2. 由于 HashSet 被限制为仅包含唯一条目,因此内部结构针对搜索进行了优化(与列表相比)- 速度要快得多

  3. 添加到 HashSet 会返回一个布尔值 - 如果由于 Set 中已经存在而添加失败,则返回 false

  4. 可以对集合执行数学集合运算:Union/Intersection/IsSubsetOf 等。

  5. HashSet 没有实现 IList only ICollection

  6. 您不能将索引与 HashSet 一起使用,只能使用枚举数。

使用 HashSet 的主要原因是如果您对执行 Set 操作感兴趣。

给定 2 个集合:hashSet1 和 hashSet2

 //returns a list of distinct items in both sets
 HashSet set3 = set1.Union( set2 );

与使用 LINQ 的等效操作相比,效果非常好。写起来也更整洁!

【讨论】:

  • IDK,Union 方法有问题。我改用了UnionWith
  • +1 表示“使用 HashedSet 的主要原因是您对执行 Set 操作感兴趣。”
  • 实际上,我更喜欢指出 HashSets 适用于您可以将您的收藏视为“包物品”的情况的答案。集合操作不像遏制检查那么频繁。在任何时候你有一组独特的项目(例如代码)并且你需要检查是否包含,HashSet 很方便。
  • 好答案。我也很想添加一些性能特征差异。
  • 问题:主要原因是不能确定没有重复项?
【解决方案2】:

为了更准确,让我们用例子来演示,

你不能像下面的例子那样使用 HashSet。

HashSet<string> hashSet1 = new HashSet<string>(){"1","2","3"};
for (int i = 0; i < hashSet1.Count; i++)
    Console.WriteLine(hashSet1[i]);

hashSet1[i] 会产生错误:

无法将 [] 索引应用于类型表达式 'System.Collections.Generic.HashSet'

你可以使用foreach语句:

foreach (var item in hashSet1)
    Console.WriteLine(item);

您不能将重复的项目添加到 HashSet 而 List 允许您这样做并且 在向 HashSet 添加项目时,可以检查它是否包含该项目。

HashSet<string> hashSet1 = new HashSet<string>(){"1","2","3"};
if (hashSet1.Add("1"))
   Console.WriteLine("'1' is successfully added to hashSet1!");
else
   Console.WriteLine("'1' could not be added to hashSet1, because it contains '1'");

HashSet 有一些有用的函数,如IntersectWithUnionWithIsProperSubsetOfExceptWithSymmetricExceptWith 等。

IsProperSubsetOf:

HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "4" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" };
HashSet<string> hashSet3 = new HashSet<string>() { "1", "2", "3", "4", "5" };
if (hashSet1.IsProperSubsetOf(hashSet3))
    Console.WriteLine("hashSet3 contains all elements of hashSet1.");
if (!hashSet1.IsProperSubsetOf(hashSet2))
    Console.WriteLine("hashSet2 does not contains all elements of hashSet1.");

UnionWith:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" };
hashSet1.UnionWith(hashSet2); //hashSet1 -> 3, 2, 4, 6, 8

IntersectWith:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4", "8" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" }
hashSet1.IntersectWith(hashSet2);//hashSet1 -> 4, 8

ExceptWith

 HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "5", "6" };
 HashSet<string> hashSet2 = new HashSet<string>() { "1", "2", "3", "4" };
 hashSet1.ExceptWith(hashSet2);//hashSet1 -> 5, 6

SymmetricExceptWith

 HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "5", "6" };
 HashSet<string> hashSet2 = new HashSet<string>() { "1", "2", "3", "4" };
 hashSet1.SymmetricExceptWith(hashSet2);//hashSet1 -> 4, 5, 6

顺便说一下,HashSets 中没有保留顺序。在示例中,我们最后添加了元素“2”,但它是第二个顺序:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4", "8" };
hashSet1.Add("1");    // 3, 4, 8, 1
hashSet1.Remove("4"); // 3, 8, 1
hashSet1.Add("2");    // 3, 2 ,8, 1

【讨论】:

    【解决方案3】:

    HashSet&lt;T&gt; 是一个类,旨在为您提供 O(1) 查找容器(即,此集合是否包含特定对象,并快速告诉我答案)。

    List&lt;T&gt; 是一个类,旨在为您提供一个具有O(1) 随机访问的集合,而不是可以动态增长(想想动态数组)。您可以在O(n) 时间测试遏制(除非列表已排序,否则您可以在O(log n) 时间进行二分查找)。

    也许您可以举例说明在哪些情况下HashSet&lt;T&gt; 应该优于List&lt;T&gt;

    当您想在O(1) 中测试收容时。

    【讨论】:

    • 如果列表已排序,则为 O(log n);毕竟,它比在未排序的列表中查找要快。
    【解决方案4】:

    如果你想使用List&lt;T&gt;

    • 按特定顺序存储项目集合。

    如果你知道你想要的项目的索引(而不是项目本身的值)检索是O(1)。如果您不知道索引,则查找该项目需要更多时间,O(n) 对于未排序的集合。

    如果你想使用Hashset&lt;T&gt;

    • 快速找出某个对象是否包含在集合中。

    如果您知道要查找的东西的名称,查找是O(1)(这是“哈希”部分)。它不像List&lt;T&gt; 那样保持顺序,并且您不能存储重复项(添加重复项没有效果,这是“设置”部分)。

    何时使用Hashset&lt;T&gt; 的一个例子是,如果您想查明在拼字游戏中玩的单词是否是英语(或其他语言)中的有效单词。如果您想构建一个供此类游戏的在线版本的所有实例使用的 Web 服务,那就更好了。

    List&lt;T&gt; 将是一个很好的数据结构,用于创建记分牌以跟踪玩家得分。

    【讨论】:

      【解决方案5】:

      List 是一个有序列表。这是

      • 由整数索引访问
      • 可以包含重复项
      • 有一个可预测的顺序

      HashSet 是一个集合。它:

      • 可以阻止重复项(请参阅Add(T)
      • 不保证集合内物品的顺序
      • 在集合上具有您期望的操作,例如、IntersectWith、IsProperSubsetOf、UnionWith。

      当您想要访问您的集合时,List 更合适,就好像它就像一个您可以追加、插入和删除项目的数组一样。如果您想将您的集合视为顺序不重要的“袋子”项目,或者当您想使用 IntersectWith 或 UnionWith 等操作将其与其他集合进行比较时,HashSet 是一个更好的选择。

      【讨论】:

        【解决方案6】:

        List 不一定是唯一的,而 hashset 是唯一的。

        【讨论】:

          【解决方案7】:

          列表是类型 T 对象的有序集合,与数组不同,您可以添加和删除条目。

          您将使用一个列表,您希望按照存储顺序引用成员,并且您通过位置而不是项目本身来访问它们。

          HashSet 就像一个字典,item 本身既是 key 也是 value,顺序是不确定的。

          您可以使用 HashSet 来检查对象是否在集合中

          【讨论】:

          • 澄清一下,以防其他人第一眼就读错了——List 维护一个顺序(即添加内容时),但不会自动对项目进行排序。您必须致电 .Sort 或使用 SortedList
          【解决方案8】:

          如果您决定将这些数据结构应用到数据驱动开发中的实际使用中,HashSet 在测试针对数据适配器源的复制、数据清理和迁移方面非常有帮助。

          另外,如果使用 DataAnnotations 类,可以在类属性上实现 Key 逻辑,并使用 HashSet 有效地控制自然索引(集群或非集群),这在 List 实现中是非常困难的。

          使用列表的一个很好的选择是在视图模型上实现多种媒体的泛型,例如将类列表发送到 MVC 视图以获取 DropDownList 助手,以及通过 WebApi 作为 JSON 构造发送。该列表允许典型的类集合逻辑,并为更类似于“接口”的方法保持灵活性,以将单个视图模型计算到不同的介质。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-09-28
            • 2012-03-10
            • 1970-01-01
            • 2021-04-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-09-01
            相关资源
            最近更新 更多