关于java:HashSet vs LinkedHashSet

HashSet vs LinkedHashSet

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

A LinkedHashSet is an ordered version of HashSet that
maintains a doubly-linked List across all elements. Use this class instead of HashSet
when you care about the iteration order. When you iterate through a HashSet the
order is unpredictable, while a LinkedHashSet lets you iterate through the elements
in the order in which they were inserted.

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


答案在于LinkedHashSet用于构造基类的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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构造函数,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * 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);
}


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

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

如您所见,内部地图是LinkedHashMap。如果您查看LinkedHashMap,您将发现以下字段:

1
private transient Entry<K, V> header;

这是有问题的链表。


HashSet是无序和未排序的Set。 LinkedHashSet是HashSet的有序版本.HashSet和LinkedHashSet之间的唯一区别是LinkedHashSet维护插入顺序。当我们遍历HashSet时,顺序是不可预测的,而在LinkedHashSet的情况下它是可预测的。
LinkedHashSet维护插入顺序的原因是底层数据结构是双向链表。


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


我建议你大部分时间都使用LinkedHashSet,因为它总体上有更好的性能):

  • 可预测的迭代顺序LinkedHashSet(Oracle)
  • LinkedHashSet的插入比HashSet更昂贵;
  • 一般来说性能比HashMap略好,因为大多数时候我们使用Set结构进行迭代。
  • Performance tests:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    ------------- 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

    您可以在此处查看源测试页面:最终性能测试示例


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

    1
    2
    3
    4
    5
    6
    7
    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
    2
    3
    1
    ab
    2

    LinkedHashSet输出

    1
    2
    3
    2
    1
    ab


    HashSet的:
    实际上是无序的。
    如果你通过参数意味着

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

    出局:
    可能2,1,3无法预测。下次再订购。

    LinkedHashSet()产生FIFO订单。


    HashSet的:

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

    In HashSet duplicates are not allowed.if users are trying to insert duplicates when we won't get any compile or runtime exceptions. add method returns simply false.

    构造函数:

    HashSet h=new HashSet(); creates an empty HashSet object with default initial capacity 16 and default fill ratio(Load factor) is 0.75 .

    HashSet h=new HashSet(int initialCapacity); creates an empty HashSet object with specified initialCapacity and default fill ration is 0.75.

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

    HashSet h=new HashSet(Collection c); creates an equivalent HashSet object for the given collection. This constructor meant for inter conversion between collection object.

    LinkedHashSet:

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

    差异
    HashSet的:

  • 带下划线的数据结构是Hashtable。
  • 插入顺序不会保留。
  • 介绍1.2版本。
  • LinkedHashSet:

  • 带下划线的数据结构是LinkedList和Hashtable的组合。
  • 插入订单已保留。
  • 产生于1.4版本。

  • 如果你看一下从LinkedHashSet类调用的构造函数,你会发现在内部它是一个用于支持目的的LinkedHashMap


    所有方法和构造函数都相同,但只有一个区别是LinkedHashset将维护插入顺序,但不允许重复。

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