【问题标题】:HashSet vs LinkedHashSetHashSet 与 LinkedHashSet
【发布时间】:2011-07-02 02:58:49
【问题描述】:

它们之间有什么区别?我知道

LinkedHashSet 是 HashSet 的有序版本,它 维护所有元素的双向链表。使用此类而不是 HashSet 当您关心迭代顺序时。当您遍历 HashSet 时 顺序是不可预测的,而 LinkedHashSet 允许您遍历元素 按照它们插入的顺序。

但是LinkedHashSet的源码中只有HashSet的调用构造函数。那么双链表和插入顺序在哪里呢?

【问题讨论】:

  • 使用 Intellij(Ctrl + B) 选项来追踪答案。 :)
  • 当然你需要附上源代码。 :)

标签: java hashset linkedhashset


【解决方案1】:

答案在于LinkedHashSet使用了哪些构造函数来构造基类:

public LinkedHashSet(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor, true);      // <-- boolean dummy argument
}

...

public LinkedHashSet(int initialCapacity) {
    super(initialCapacity, .75f, true);            // <-- boolean dummy argument
}

...

public LinkedHashSet() {
    super(16, .75f, true);                         // <-- boolean dummy argument
}

...

public LinkedHashSet(Collection<? extends E> c) {
    super(Math.max(2*c.size(), 11), .75f, true);   // <-- boolean dummy argument
    addAll(c);
}

描述了一个采用布尔参数的HashSet 构造函数(一个示例),如下所示:

/**
 * Constructs a new, empty linked hash set.  (This package private
 * constructor is only used by LinkedHashSet.) The backing
 * HashMap instance is a LinkedHashMap with the specified initial
 * capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hash map
 * @param      loadFactor        the load factor of the hash map
 * @param      dummy             ignored (distinguishes this
 *             constructor from other int, float constructor.)
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero, or if the load factor is nonpositive
 */
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}

【讨论】:

  • 父类具有明确的子类功能,忽略的参数来区分
  • 不完全是使用虚拟参数进行构造函数消歧的简洁设计。
  • 这是相当干净的设计,因为 API 是干净的(这个 HashSet 构造函数是包私有的)。实现细节对于类的用户来说并不重要。维护此代码可能更难,但对于 java.util 类,即使是非常小的性能改进也可以证明这一点。
【解决方案2】:

HashSetunordered 并且 unsorted Set。
LinkedHashSet有序版本 的 HashSet。

HashSetLinkedHashSet 的唯一区别在于:
LinkedHashSet 维护插入顺序。

当我们遍历 HashSet 时,顺序是不可预测的,而在 LinkedHashSet 的情况下它是可预测的。

LinkedHashSet 维护插入顺序的原因是:
底层使用的数据结构是双链表

【讨论】:

    【解决方案3】:

    LinkedHashSet 的构造函数调用以下基类构造函数:

    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
      map = new LinkedHashMap<E, Object>(initialCapacity, loadFactor);
    }
    

    如您所见,内部映射是LinkedHashMap。如果您查看LinkedHashMap 内部,您会发现以下字段:

    private transient Entry<K, V> header;
    

    这是有问题的链表。

    【讨论】:

      【解决方案4】:

      我建议你大部分时间使用LinkedHashSet,因为它整体性能更好):

      1. 可预测 迭代订单LinkedHashSet (Oracle)
      2. LinkedHashSet 的插入成本比 HashSet 高;
      3. 总体而言,性能略好于 HashMap,因为大多数时候我们使用 Set 结构进行迭代。

      性能测试:

      ------------- TreeSet -------------
       size       add  contains   iterate
         10       746       173        89
        100       501       264        68
       1000       714       410        69
      10000      1975       552        69
      ------------- HashSet -------------
       size       add  contains   iterate
         10       308        91        94
        100       178        75        73
       1000       216       110        72
      10000       711       215       100
      ---------- LinkedHashSet ----------
       size       add  contains   iterate
         10       350        65        83
        100       270        74        55
       1000       303       111        54
      10000      1615       256        58
      

      您可以在此处查看源测试页面:The Final Performance Testing Example

      【讨论】:

      • 在这些“基准”之前,我没有看到 JVM 有任何预热,所以我不会认真对待这些数据。 Read more
      【解决方案5】:

      您应该查看它调用的 HashSet 构造函数的来源...它是一个特殊的构造函数,它使支持 Map 成为 LinkedHashMap 而不仅仅是 HashMap

      【讨论】:

      • 谢谢,HashSet中有构造LinkedHashMap的构造函数,在LinkedHashSet中调用,所有逻辑都在LinkedHashMap中
      【解决方案6】:

      哈希集: 实际上是无序的。 如果你传递参数意味着

      Set<Integer> set=new HashSet<Integer>();
      for(int i=0;i<set.length;i++)
      {
        SOP(set)`enter code here`
      }
      

      输出: 可能是2,1,3 不可预测。下次再下单。

      LinkedHashSet() 产生先进先出顺序。

      【讨论】:

        【解决方案7】:

        哈希集:

        下划线的数据结构是Hashtable。 不允许重复的对象。插入顺序不保留,它基于对象的哈希码。 可以进行空值插入(仅一次)。 它实现了 Serializable、Clonable 但不是 RandomAccess 接口。 如果频繁操作是搜索操作,最好选择HashSet。

        在 HashSet 中不允许重复。如果用户尝试插入重复项,则我们不会得到任何编译或运行时异常。 add 方法只返回 false。

        构造函数:

        HashSet h=new HashSet();创建一个空的 HashSet 对象,默认初始容量为 16,默认填充率(负载因子)为 0.75。

        HashSet h=new HashSet(int initialCapacity);使用指定的 initialCapacity 创建一个空的 HashSet 对象,默认填充率为 0.75。

        HashSet h=new HashSet(int initialCapacity, float fillRatio);

        HashSet h=new HashSet(Collection c);为给定的集合创建一个等效的 HashSet 对象。此构造函数用于集合对象之间的相互转换。

        LinkedHashSet:

        它是 HashSet 的子类。除了以下不同之外,它与 HashSet 完全相同,包括(构造函数和方法)。

        差异 哈希集:

        1. 下划线的数据结构是Hashtable。
        2. 不保留插入顺序。
        3. 引入了 1.2 版本。

        LinkedHashSet:

        1. 带下划线的数据结构是 LinkedList 和 Hashtable 的组合。
        2. 保留插入顺序。
        3. 在 1.4 版本中引入。

        【讨论】:

          【解决方案8】:

          HashSet 不维护插入项的顺序
          LinkedHashSet维护插入项的顺序

          例子

          Set<String> set = ...;// using new HashSet<>() OR new LinkedHashSet<>()
          set.add("2");
          set.add("1");
          set.add("ab");
          for(String value : set){
             System.out.println(value);
          }  
          

          HashSet 输出

          1
          ab
          2
          

          LinkedHashSet 输出

          2
          1
          ab
          

          【讨论】:

            【解决方案9】:

            如果您查看从 LinkedHashSet 类调用的构造函数,您会发现在内部它是用于支持目的的 LinkedHashMap

            【讨论】:

              【解决方案10】:

              所有方法和构造函数都相同,但唯一不同的是 LinkedHashset 将保持插入顺序,但不允许重复。

              Hashset 不会维护任何插入顺序。 它是 List 和 Set 的简单组合:)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-08-30
                • 2020-11-26
                • 2013-09-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-12-05
                • 2011-01-11
                相关资源
                最近更新 更多