1. 程式人生 > >Java集合框架學習

Java集合框架學習

今天看了一下集合框架,其實指的就是Collection介面和Map介面,其中Collection介面是Iterable介面的子介面。而Map介面上面已經沒有父類介面了。
其中,Iterable介面包含三個方法

一、Collection介面
從Collection介面,就是我們的框架內容了,大致將一下,Collection介面下面有很多的子介面,其他的沒用過,但是有三個我們經常碰見的,就是Set , List,Queue
List:是一種底層是陣列實現的, 元素可重複的有序集合,下面有很多實現,比如ArrayList,LinkedList , Vector等
Set:底層是hash表實現的,不可重複的無序集合,

下面的實現有HashSet,TreeSet等
Queue:用於裝載先進先出原則的資料 ,下面的實現有Dequeue,LinkedList等。
這裡寫圖片描述

下面來介紹集中常用的實現
1、ArrayList,最常用的集合,底層是陣列,原始碼中的定義如下,和父類List介面一直,是可以存放重複的元素 ,且存放的元素是有序的。

 /**
   * The array buffer into which the elements of the ArrayList are stored.
    * The capacity of the ArrayList is the length of this array buffer. Any
    * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    * will be expanded to DEFAULT_CAPACITY when the first element is added.
    */
transient Object[] elementData; // non-private to simplify nested class access

ArrayList與普通的陣列有什麼區別呢?
最大的區別就是陣列的長度是固定的,不可變的,一旦超出陣列的長度範圍是會報錯的,而ArrayList底層雖然也是陣列,但是長度卻是可變的,每次增加元素是,也就是add方法,都將會檢查一遍上面定義的這個陣列的長度是否足夠,不夠的話,將新建一個新的陣列,並將老的陣列複製到新的陣列當中。

    public boolean add(E e) {
        ensureCapacityInternal(size + 1
); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0)//如果下標大於陣列長度,則需要新建一個數組 grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }

第二點就是,集合做到動態擴容犧牲的是自身的效率,因此,陣列的效率是比集合要高的。
第三,集合定義了remove()方法刪除自身元素,而陣列沒有。
第四點,集合存入物件時,拋棄型別資訊,所有物件遮蔽為Object,編譯時不檢查型別,但是執行時會報錯。而陣列一旦定義好了,如果存放不同型別的資料,編譯就會不通過。這個區別總結起來就是,陣列只能存放單一型別,而集合可以存放任意型別,預設就是Object,所有物件。

當然了,集合和陣列也可以轉換,只限於集合存放單一型別
集合–>陣列,List.toArray()
陣列–>集合,Array.asList()

2、LinkedList

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

看定義我們就知道,LinkedList是實現了List和Queue介面的。底層是連結串列實現的。LinkedList通過代價較低的在List中間進行插入和刪除操作,提供了優化的順序訪問。也即是說,相比於ArrayList,LinkedList的插入刪除速度是比ArrayList慢的,而查詢的速度卻更加的快。
原始碼中,unlinkLast()方法和unlinkFirst(),顧名思義,也就是pollLat()he pollFirst()的呼叫,裡面是這麼寫的:

private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

    /**
     * Unlinks non-null last node l.
     */
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

裡面的這句help GC很有意思,意思是讓某個物件=null,就能被GC掉,否則這個物件永遠不會被GC,為什麼呢?翻翻JVM書可以找到答案。

3、Vector
List的另一種實現,使用方法和ArrayList一樣,但是,人家是執行緒安全的,所有操作元素的方法都加了synchronized關鍵字,這也是兩者的最大的區別。
兩者還有一個不同點,就是兩者的擴容方式:
ArrayList:初始容量*3/2+1
Vector:預設擴充兩倍。

4、HashSet
說HashSet之前先看看下面的HashMap,因為HashSet是對HashMap的一層包裝,內部就是一個HashMap,沒什麼區別。先看HashMap。

5、HashMap
Map介面的實現類。以key/value鍵值對存放資料,key值不允許為空,value可以為空,且由於容器可能會對元素進行重新hash,HashMap無法保證元素是有序的。
重新Hash之後,有可能會產生衝突,HashSet處理衝突時候,採用的是衝突連結串列方式
HashMap的效能問題,一般來說,put和get是可以在常數時間內完成,但是,如果要對HashMap進行迭代,就需要去遍歷整個HashMap和衝突連結串列。因此,如果是迭代次數比較多的場景,HashMap的初始值不應該設定太大。
有兩個引數可以影響HashMap的效能,一個是初始容量(inital capacity)和負載係數(load factor),負載係數指的是自動擴充容量的臨界值。當entry的數量大於(初始容量*和負載係數)時,HashMap將進行擴容,且重新計算Hash。所以,對於插入操作較多的場景,將初始值設定稍大,可以減少重新Hash的次數。
另外,將物件放到HashMap中時,需要注意兩個方法,hashCode()和equals(),當插入物件時,是通過hashCode計算Hash值判斷物件應該插入到什麼位置,當Hash值重複時,就要通過equals()方法去判斷是不是同一個物件。所以說,如果要將自定義的物件放入到HashMap中,需要重寫這兩個方法。

6、HashTable
也是Map的實現類,與HashMap一樣,唯一不同的就是,HashTable是同步的。