【Java基礎】集合中的List

一、什麼是List

List是一個有序的集合,和set不同的是,List允許存儲項的值爲空,也允許存儲相等值的存儲項。
在這裏插入圖片描述
List繼承了 Collection的接口,因此包括 Collection提供的各種方法,初次之外還擴展了自己的方法,如下圖所示。
在這裏插入圖片描述

二、List的分類

(一)ArrayList

1、什麼是ArrayList

ArrayList是一個數組實現的列表,底層是數組,由於數據是存入數組中的,所以它的特點也和數組一樣,查詢很快,但是中間部分的插入和刪除很慢。

2、與Array的區別
  1. ArrayList內部封裝了一個Object類型的數組,所以本質上沒有區別,ArrayList的很多方法都是直接在內部數組的基礎上調用的Array的方法。但是ArrayList還有其他功能因此更加複雜。
  2. ArrayList可以存儲不同類型的數據,但是Array只能存儲相同數據類型的數據。
  3. Array的長度不可變,ArrayList是變長的,雖然底層是通過擴展數據長度來實現的,但是表面上看是變長的。
  4. 存取和增刪元素
    對於一般的引用類型來說,這部分的影響不是很大,但是對於值類型來說,往ArrayList裏面添加和修改元素,都會引起裝箱和拆箱的操作,頻繁的操作可能會影響一部分效率。另外,ArrayList是動態數組,它不包括通過Key或者Value快速訪問的算法,所以實際上調用IndexOf、Contains等方法是執行的簡單的循環來查找元素,所以頻繁的調用此類方法並不比你自己寫循環並且稍作優化來的快,如果有這方面的要求,建議使用Hashtable或SortedList等鍵值對的集合。
3、方法探究
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.默認長度
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 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 == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
     //從這裏可以看到,ArrayList的底層是由數組實現的,並且默認數組的默認大小是10
    private transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

ArrayList的構造函數
ArrayList有2個構造函數,一個是默認無參的,一個是有參的(數組大小)
如果我們可以預估數組大小,最好使用使用有參構造函數。因爲如果使用無參的構造函數,會首先把EMPTY_ELEMENTDATA(默認是10)賦值給elementData
然後根據插入個數於當前數組size比較,不停調用Arrays.copyOf()方法,擴展數組大小造成性能浪費。

public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }

add操作

//從尾部插入
 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,把index開始所有數據向後移動一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
         //將新元素插入到index位置
        elementData[index] = element;
        size++;
    }

     private void ensureCapacityInternal(int minCapacity) {
     	//如果數組爲空
            if (elementData == 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/2),但性能會好一些,
        int newCapacity = oldCapacity + (oldCapacity >> 1);//根據老數組長度計算新數組長度,默認每次都是自增50%的大小
        //下面判斷保證新數組元素的個數在正常範圍內
        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);//用新數組繼續下面的處理
    }

(二)Vector

Vector可以看做是線程安全版的ArrayList,利用synchronized來實現線程安全,所以他的一大弊端就是性能不高,而且這個類太古老已經被棄用。
因此如果我們對線程安全性要求不高就可以使用ArrayList,如果必須保證線程安全可以使用Collections中提供的方法,轉變成線程安全的類。

(三)LinkedList

LinkedList底層是一個雙向鏈表。LinkedList繼承於AbstractSequentialList,和ArrayList一個套路。內部維護了3個成員變量,一個是當前鏈表的頭節點,一個是尾部節點,還有是鏈表長度。

方法探究

add(E e)操作

//從指定位置插入(中間插入)
 public void add(int index, E element) {
        checkPositionIndex(index);
//如果插入位置和size相等相當於尾部插入
        if (index == size)
            linkLast(element);
        else
        //否則就是中部插入
            linkBefore(element, node(index));
    }
//尾部插入
 public boolean add(E e) {
 //尾部插入的具體實現
        linkLast(e);
        return true;
    }

 void linkLast(E e) {
 //記錄當前最後一個節點
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        //將新增節點賦值給尾節點
        last = newNode;
       //如果尾節點爲空,說明沒有元素,那麼直接將新增元素賦值給頭結點
        if (l == null)
            first = newNode;
        else
        原來尾節點的next指向新增的節點
            l.next = newNode;
            //數量加一
        size++;
        modCount++;
    }

 void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        //pred爲插入節點的指向的前一個位置
        final Node<E> pred = succ.prev;
        //新增的節點
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        //如果指向前一個爲空則說明這個鏈表爲空,將新節點賦給頭結點
        if (pred == null)
            first = newNode;
        else
        //否則將原來index的pred的next指向新節點
            pred.next = newNode;
            //數量加1
        size++;
        modCount++;
    }

indexOf操作
從頭開始遍歷整個鏈表,如果沒有就反-1,有就返回當前下標

public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

remove操作

//無參的刪除方法默認刪除頭結點
public E remove() {
        return removeFirst();
    }
//刪除中間節點
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
//刪除頭結點
     public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    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;
    }

    //x的prev指向x的next
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

   
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        //刪除頭節點非常簡單,就是把頭節點的值清空,next清空
        f.item = null;
        f.next = null; // help GC
        //把nextNode設爲頭節點
        first = next;
        if (next == null)
            last = null;
        else
        //清空next的prev
            next.prev = null;
            //size減1
        size--;
        modCount++;
        return element;
    }

三、總結

List的特性:

  • 按順序查找
  • 可以存null值
  • 可以存重複的值

對比LinkedList和ArrayList的實現方式不同,可以在不同的場景下使用不同的List 。ArrayList是由數組實現的,方便查找,返回數組下標對應的值即可,適用於多查找的場景 ,LinkedList由鏈表實現,插入和刪除方便,適用於多次數據替換的場景。
ArrayList、LinkedList、Vector的區別和實現原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章