從源碼的角度分析List與Set的區別

不少時候咱們在討論List與Set的異同點時都在說:數組

  一、List、Set都實現了Collection接口this

  二、List是有序的,能夠存儲重複的元素,容許存入nullspa

  三、Set是無序的,不容許存儲重複的元素,只容許存入一個nullcode

  四、List查詢效率高,但插入刪除效率低orm

  五、Set檢索元素效率低、但刪除插入效率高對象

  六、List能夠經過索引操做元素,Set不能根據索引獲取到元素blog

這裏基於ArrayList/HashSet(jdk1.8)的角度進行分析二者的異同點:繼承

1、從各自所繼承的父類以及實現接口,可知二者的源頭是一致的,都是從Collection接口延伸出來索引

 

2、建立ArrayList實例接口

不指定初始容量時,建立一個空的對象數組,與原有註釋不一致,註釋寫着建立一個長度爲10的數組,實際是在添加元素時進行判斷處理的。還能夠指定容量建立相應長度的數組、也能夠傳入一個集合來進行建立,此處就再也不詳細列出

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

由建立實例可知,ArrayList底層是由數組實現的,因是數組的方式實現,而數組實際是有序數據的集合,因此List也就相應是有序的,且也是能存入null。

3、建立HashSet實例

在不指定容量的狀況下建立HashSet時,直接是去建立一個HashMap實例,用HashMap來實現HashSet的相關功能。能夠指定容量和負載因子等進行建立,實際也是HashMap的其餘構造方法

 /**
  * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
  * default initial capacity (16) and load factor (0.75).
  */
 public HashSet() {
     map = new HashMap<>();
 }

由建立實例可知,底層實現爲HashMap,既然是HashMap,按HashMap的規則,咱們也就可知HashSet是無序的,由於HashMap的存儲方式是先用key進行hash找到相應位置,而後再在該位置存儲對應的key和value。HashSet不容許存儲重複元素,是由於HashSet在添加元素時,是用該元素做爲key,一個空的對象做爲value進行存儲的,也就相應的只能存儲一個null

具體可見下圖:

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
 * Adds the specified element to this set if it is not already present.
 * More formally, adds the specified element <tt>e</tt> to this set if
 * this set contains no element <tt>e2</tt> such that
 * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
 * If this set already contains the element, the call leaves the set
 * unchanged and returns <tt>false</tt>.
 *
 * @param e element to be added to this set
 * @return <tt>true</tt> if this set did not already contain the specified
 * element
 */
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

4、List查詢效率高,但插入刪除效率低

a、ArrayList是數組的方式實現,數組在內存中是連續且成塊存儲的,在查詢時直接根據索引來獲取數組相應值,因此查詢效率會比較高

/**
 * Returns the element at the specified position in this list.
 *
 * @param  index index of the element to return
 * @return the element at the specified position in this list
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

b、在作插入和刪除操做時,若是直接添加一個元素,不指定索引時,直接添加到數組的末尾,也會比較快,若是須要按索引去添加時就須要對原有數組的相應後續元素進行復制移位再進行對該索引對應的位置進行賦值,刪除一樣,把該索引的後續元素複製前移一位,而後把最後一個索引置空待GC,所以插入和刪除操做效率會相對較低

//直接添加元素,在數組的最後添加(在添加元素以前,先判斷數組長度是否知足,若是第一次添加,則建立一個長度爲10的數組,若是非第一次添加,則判斷數組長度是否充足,若是不長度不夠則建立一個長度爲(oldCapacity + (oldCapacity >> 1))的新數組,
//並把舊舒服複製到新的數組中來)
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } //先判斷索引是否有效,再判斷數組長度是否充足,不充足則參照上面流程添加新數組,再進行數組的複製移位 public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } //如上面解釋 public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }

5、Set檢索元素效率低、但刪除插入效率高

HashSet沒有get單個值得方法。在檢索元素時,須要先獲取map的全部數據,再進行遍歷比對,因此效率會比ArrayList低。而在刪除插入時,根據map的特性,只須要要對所插入的對象做爲key進行hash找到相應位置,而後放入該元素便可,因此相對ArrayList的須要進行數組的複製移位來講效率會相對較高

//如上面解釋,HashSet的remove實際也就是map的remove
public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

6、List能夠經過索引操做元素,Set不能根據索引獲取到元素

由上面分析可知,ArrayList是數組實現,存在索引可操做元素,而HashSet是HashMap實現,須要對元素進行hash找到位置再存儲,不存在直接索引獲取的方式

相關文章
相關標籤/搜索