【问题标题】:HashSet vs. ArrayListHashSet 与 ArrayList
【发布时间】:2013-08-01 03:57:16
【问题描述】:

所以我有一个自定义班级 Class,它将有一组另一个自定义班级学生。所以它看起来像这样:

public class Class {
    private Set<Student> students;

    // other methods
}

现在我将向学生集添加和删除许多学生,并且我还将更改学生集中已经存在的学生的许多私有字段。

问题:我应该使用什么数据结构来最好地实现这一点?由于我将更改学生集中的 Student 对象的属性(从而更改哈希码),我应该使用 ArrayList 来代替吗?

【问题讨论】:

  • 你打算如何查找学生来修改他们 - 这将建议他们应该如何存储 - 地图与列表。
  • 如果你在 Student 中有一个像 ID 这样的“主键”并且你不改变它,那么你就没有 prlbem,使用一个集合
  • 在给你的班级起的所有名字中,Class 可以说是最愚蠢的选择。

标签: java arraylist collections set hashset


【解决方案1】:

当谈到ArrayListHashSet 的行为时,它们是完全不同的类。

数组列表

  • ArrayList 不验证重复项。
  • get()O(1)
  • contains()O(n),但您可以完全控制条目的顺序。

                          get  add  contains next remove(0) iterator.remove
    ArrayList             O(1) O(1) O(n)     O(1) O(1)      O(1)
    
  • 不是线程安全的,要使其线程安全,您必须使用Collections.synchronizedList(...)

哈希集

  • HashSet 确保没有重复。
  • 为您提供O(1) contains() 方法,但不保留顺序。

                          add      contains next     notes
    HashSet               O(1)     O(1)     O(h/n)   h is the table 
    
  • 不是线程安全的,要使其线程安全,您必须使用Collections.synchronizedSet(...)

【讨论】:

  • 更正: ArrayList get() 复杂度是O(1),而不是O(n)
  • 是的,您是正确的,感谢您的更新。下次请使用参考文献,这样同行评审员会更容易。
【解决方案2】:

我应该使用什么数据结构来最好地实现这一点?由于我将更改学生集中的 Student 对象的属性(从而更改哈希码),我应该改用 ArrayList 吗?

如果集合元素的哈希码可能会更改,那么您不应该使用HashSet。 (如果这样做,数据结构将中断,并且集合中的元素可能会丢失。)

但我怀疑你是否应该使用ArrayList,因为如果hashcode() 对对象的更改敏感,那么equals(Object) 很可能也是如此。这意味着contains(...) 和类似方法将无法找到对象。

我认为您应该使用Map 类型,并使用“学生标识符”作为键。

(您也可以覆盖hashcodeequals,这样相等意味着两个对象具有相同的ID。但这使得equals(Object) 无法用于其他目的。)

【讨论】:

    【解决方案3】:

    如果您的代码中有重复数据,那么您应该使用 ArrayList 否则您可以使用 hashset,如下所示 因此,如果您的代码不需要重复值,则使用 Set 而不是 list 因为 set 将提供更好的性能(O(n) vs O(n^2) 的列表),这是正常的,因为避免重复是集合的目的。

    数组列表

    public static void main(String[] args) {

    ArrayList arr =new ArrayList();
    arr.add("Hello");
    arr.add("is");
    arr.add("Hello");
    System.out.println(arr);  //As we are using Arraylist therefore 
                              //the duplicate elements are allowed therefore
                              //"Hello" is not removed in the output
        
    

    }

    哈希集

    public static void main(String[] args) {

    HashSet arr =new HashSet();
    arr.add("Hello");
    arr.add("is");
    arr.add("Hello");
    System.out.println(arr);  //As we are using Hashset therefore 
                              //the duplicate elements removed therefore
                              //"Hello" is removed in the output
        
    
        
    

    }

    【讨论】:

      【解决方案4】:

      这取决于。当您谈论学生时,必须有像 id 或 rollno 这样的东西是独一无二的。如果是,则覆盖哈希码方法并根据他们的 id 实现哈希码。那么通过更改 student 的任何其他属性都不会影响哈希码。

      选择 Set 或 List 完全取决于您的要求。阅读此链接,它将阐明 Set 和 list 之间的区别
      What is the difference between Set and List?

      如果您在 Set 中使用对象,那么您可以尝试同时覆盖 hashcode and the equals method,以便控制唯一性。

      【讨论】:

        【解决方案5】:

        Set 的 javadoc 说

        注意:如果使用可变对象作为集合,必须非常小心 元素。 如果一个集合的值未指定,则集合的行为 对象以影响等于比较的方式更改,而 对象是集合中的一个元素。 this 的一个特例 禁止是不允许集合包含自身 作为一个元素。

        因此,如果您打算使用HashSet,如果您使用不可变字段制作hashCode()equals(),那么您将不会遇到这个问题。例如,为每个实例使用唯一的 studentID。

        【讨论】:

          【解决方案6】:

          根据您的要求,我认为最好的结构应该是 Map。 Set 实际上底层使用内部的 Map 结构,并且您还需要注意 equals 方法的覆盖以更好地查找。而 set 和 arraylist 查找目标对象需要一些查找算法,因此它没有您预期的那么高效(尤其是在非常大的集合情况下)。即使map也会浪费一些空间,但是如果你的ID是某种原始类型,你可以考虑Trove library中map实现的原始类型。

          【讨论】:

          • (我不建议在 Trove 上寻找像这样的“家庭作业”问题。内存使用“问题”与这里无关。而且,如果这是一个真正的应用程序,那么你不太可能将首先将学生数据库保存在内存中。)
          【解决方案7】:

          问题:我应该使用什么数据结构来最好地实现这一点? 由于我将更改集合中 Student 对象的属性 学生(从而更改哈希码)我应该使用 ArrayList 代替?

          如果您要更改 hashCode 或 equals 使用的值,则绝对不能使用 HashMap 或 HashSet。

          您是说要删除并添加很多内容。问题是您是否要按顺序或随机(基于索引)进行。如果按顺序添加、删除,那么绝对是 LinkedList 的最佳选择。如果你随机访问对象,那么 ArrayList 效率更高。

          【讨论】:

            【解决方案8】:

            当对象的equals 方法的结果将发生变化时,您不应使用Set。如果您通过稳定的唯一 ID 号识别学生,而 equals 只是检查该 ID,那么使用 Set 就可以了。

            请注意,HashSet 将使用hashCode 进行索引和比较,hashCode 应准确包含用于确定equals 的那些字段。

            【讨论】:

              【解决方案9】:

              对于像HashSet 这样的散列集合,键应该是immutable。 Hashset 在内部使用散列来决定存储对象的桶。并且在检索对象时,它将使用散列来查找对象桶。如果您在存储后更改对象,则可能会更改对象的哈希码,并且 Set 可能无法检索到正确的对象。如果即使在将对象添加到集合之后仍需要更改对象,那么使用散列集合不是一个好的选择。而是选择Arraylist,但请注意,使用ArrayList,您将失去快速检索所需学生的优势,因为它可能是使用集合。

              【讨论】:

              • 如何快速检索包含在 Set 中的 Student?
              • HashSet 内部使用散列,可以更快地检索对象。
              • 您能否添加一个如何从集合中检索特定学生的示例?我真的很好奇,如果这是真的,那么我一直低估了 Sets
              • @morgano 不要将Set 接口与HashSet 的性能混淆,这实际上只是一个不关心其值的HashMap。阅读 hash tables 以了解数据结构本身如何表现良好。
              • @PaulBellora 我知道 HashSet 可以与内部 HashMap 一起使用,但这并不能改变这样一个事实,即从 Set 中检索 Student 我无论如何都必须遍历 Set(与一个 ArrayList)内部 HashMap 的“优点”根本没有被使用。我真诚地虽然我错过了关于 Sets 的一些东西,但是这个答案仍然没有清楚地提到为什么你可以使用 HashSet 比使用 ArrayList 更快地检索对象。
              猜你喜欢
              • 2016-03-06
              • 1970-01-01
              • 2013-09-21
              • 2013-11-19
              • 2012-04-29
              • 1970-01-01
              • 1970-01-01
              • 2014-04-04
              • 1970-01-01
              相关资源
              最近更新 更多