首页 > 技术文章 > LinkedHashSet保存元素插入顺序的原理

Guoyutian 2016-02-09 19:41 原文

LinkedHashSet is an extended version of HashSet. HashSet doesn’t follow any order where as LinkedHashSet maintains insertion order. HashSet uses HashMap object internally to store it’s elements where as LinkedHashSet uses LinkedHashMap object internally to store and process it’s elements. In this article, we will see how LinkedHashSet works internally and how it maintains insertion order.

Let’s start with constructors of LinkedHashSet class. There are 4 constructors in LinkedHashSet class. All these constructors are simply calling to super class constructor i.e constructor of HashSet class. Below is the how the constructors are defined in LinkedHashSet class.

理解:LinkedHashSet是HashSet的扩展版本.HashSet用HashMap对象来存储元素,而LinkedHashSet用LinkedHashMap对象来存储和处理它的元素.这篇文章讲了LinkedHashSet的工作原理还有它是如何保存元素的插入顺序的.

//Constructor - 1
 
public LinkedHashSet(int initialCapacity, float loadFactor)
{
      super(initialCapacity, loadFactor, true);              //Calling super class constructor
}
 
//Constructor - 2
 
public LinkedHashSet(int initialCapacity)
{
        super(initialCapacity, .75f, true);             //Calling super class constructor
}
 
//Constructor - 3
 
public LinkedHashSet()
{
        super(16, .75f, true);                //Calling super class constructor
}
 
//Constructor - 4
 
public LinkedHashSet(Collection<? extends E> c)
{
        super(Math.max(2*c.size(), 11), .75f, true);          //Calling super class constructor
        addAll(c);

In the above code snippet, you might have noticed that all 4 constructors are calling the same super class constructor. This constructor is a package private constructor which is used only by the LinkedHashSet class. This constructor takes initial capacity, load factor and one boolean dummy value as it’s arguments. This boolean dummy value is just used to differentiate this constructor from other constructors of HashSet class which take initial capacity and load factor as their arguments. Here is the how this constructor is defined in HashSet class.

理解:上面的四个构造函数都调用了非类相同的构造函数.这个构造函数是包私有函数,只会被LinkedHashSet所用.构造函数有三个参数,最后一个布尔变量是用来区别父类中另一个构造函数的.

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

As you are seeing, this constructor internally creates one new LinkedHashMapobject. This LinkedHashMap object is used by the LinkedHashSet to store it’s elements.

LinkedHashSet doesn’t have it’s own methods. All methods are inherited from it’s super class i.e HashSet. So. all operations on LinkedHashSet work in the same manner as that of HashSet. The only change is the internal object used to store the elements. In hashSet, elements you insert are stored as keys of HashMap object. Where as in LinkedHashSet, elements you insert are stored as keys of LinkedHashMap object. The values of these keys will be the same constant i.e “PRESENT“. We have seen this in How HashSet works internally in Java.

理解:父类的这个构造函数主要用来构造一个LinkedHashMap对象,LinkedHashSet的元素都存储在这个对象中.LinkedHashSet没有自己的方法,所有的方法都是继承父类HashSet的.唯一的不同是LinkedHashSet用内部对象来存储元素.在HashSet中,你存储的元素会作为键值存储在HashMap对象中,而在LinkedHashMap中,你存储的元素会作为LinkedHashMap的键值来存储,而value都为PRESENT.

How LinkedHashSet Maintains Insertion Order?

LinkedHashSet uses LinkedHashMap object to store it’s elements. The elements you insert in the LinkedHashSet are stored as keys of this LinkedHashMap object. Each key, value pair in the LinkedHashMap are instances of it’s static inner class called Entry<K, V>. This Entry<K, V> class extends HashMap.Entry class. The insertion order of elements into LinkedHashMap are maintained by adding two new fields to this class. They are before and after. These two fields hold the references to previous and next elements. These two fields make LinkedHashMap to function as a doubly linked list.

理解:LinkedHashSet用LinkedHashMap对象来存储元素.LinkedHashMap的静态类Entry中增加了两个域,分别为before和after,分别用来存储前一个和后一个元素,这两个域使得LinkedHashMap成为一个双向链表.

private static class Entry<K,V> extends HashMap.Entry<K,V>
{
        // These fields comprise the doubly linked list used for iteration.
        Entry<K,V> before, after;
 
        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
        }

The first two fields of above inner class of LinkedHashMap – before and after are responsible for maintaining the insertion order of the LinkedHashSet. The header field of LinkedHashMap stores the head of this doubly linked list. It is declared like below,

private transient Entry<K,V> header;        //Stores the head of the doubly linked list

In LinkedHashMap, the same set of Entry objects (rather references to Entry objects) are arranged in two different manner. One is the HashMap and another one is Doubly linked list. The Entry objects just sit on heap memory, unaware of that they are part of two different data structures.

理解:header用来保存双向链表的表头,在LinkedHashMap中,实体对象用两种形式进行保存,一种是HashMap,一种是双向链表.entrty对象保存在堆当中,它是两种数据结构的基本元素.

Let’s see one example of LinkedHashSet to know how it works internally.

public class LinkedHashSetExample
{
    public static void main(String[] args)
    {
        //Creating LinkedHashSet
 
        LinkedHashSet<String> set = new LinkedHashSet<String>();
 
        //Adding elements to LinkedHashSet
 
        set.add("BLUE");
 
        set.add("RED");
 
        set.add("GREEN");   
 
        set.add("BLACK");
    }
}

Look at the below image to see how above program works.

How LinkedHashSet Works Internally In Java

理解:上面这张图展示LinkedHashSet的工作原理.

If you know how LinkedHashMap works internally, it will be easy for you to understand how LinkedHashSet works internally. Go through source code of LinkedHashSet class and LinkedHashMap class once, you will get precise understanding about how LinkedHashSet works internally in Java.

推荐阅读