Java基礎之集合List(ArrayList, LinkedList 和 Vector 的區別)

List

List是一個接口,實現類常用的有 ArrayList,LinkedList和Vector

ArrayList

1. 基於數組實現的的, 根據容量大小利用Arrays.copyOf方法實現動態擴容。
源碼

    // elementData 動態數組
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    // DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是{} 空數組, DEFAULT_CAPACITY是10
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    
    // 可見list數組的默認長度爲10
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

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

簡單的實現原理

public class ListDemo {

    // 定義初始數組
    String[] arr = {};

    // 數組擴容
    public void dilatation(int capacity) {
        arr = Arrays.copyOf(arr, capacity);

    }

    public static void main(String[] a) {
        ListDemo listDemo = new ListDemo();
        System.out.println(listDemo.arr.length);
        System.out.println(Arrays.toString(listDemo.arr));
        // 擴容後給數組賦值
        listDemo.dilatation(1);
        listDemo.arr[0] = "222";
        System.out.println(listDemo.arr.length);
        System.out.println(Arrays.toString(listDemo.arr));
          // 擴容後給數組賦值
        listDemo.dilatation(2);
        listDemo.arr[1] = "333";
        System.out.println(listDemo.arr.length);
        System.out.println(Arrays.toString(listDemo.arr));
    }

}
2. ArrayList 是有序的,可以看到新增的數據是根據size,集合的大小插入的
源碼
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

3. ArrayList 中查詢更快捷,因爲是根據數組下標進行查詢。
源碼
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
    
    E elementData(int index) {
        return (E) elementData[index];
    }
    
4. ArrayList 添加刪除操作效率低,是因爲每次增加或刪除時要對數組容量進行修改。 System.arraycopy()實現數組的複製
System.arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);

src 原數組
srcPos 原數組複製的起始位置(下標)
dest 目標數組
destPos 目標數組的起始位置(下標)
length 複製數組的長度


舉例
    public static void main(String[] a) {
        // 我要將 4,7,9 替換成了7,9,8
        Integer[] arr = {2,6,5,4,7,9,8,1,3};

        // 將 arr源數組從第下標4 開始複製3位 也就是 7,9,8  替換arr目標數組中下標3 開始的三位4,7,9。也就是將 4,7,9 替換成了7,9,8
        System.arraycopy(arr, 4, arr, 3, 3 );

        System.out.println(Arrays.toString(arr));
    }

源碼
    // 刪除
    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;
    }
    // 添加
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    // DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是{} 空數組, DEFAULT_CAPACITY是10
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    
    // 可見list數組的默認長度爲10
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        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);
    }
5. ArrayList 對於修改效率高,因爲是根據下標直接獲取到目標對象,直接修改
源碼
    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

LinkedList

LinkedList源碼中有這樣幾個對象屬性

  1. first 第一個節點
  2. last 最後一個節點

節點類有這樣幾個信息

  1. item 本身的信息
  2. next 下一個節點對象
  3. prev 上一個節點對象
源碼
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

1. 基於鏈表實現,每一個節點中都會保存當前節點,上一個節點和下一個節點。

2. 添加更快捷,因爲不需要跟ArrayList一樣需要考慮容量問題。只需要創建新的節點,就行了。

源碼
    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
            l.next = newNode;
        size++;
        modCount++;
    }

3. 刪除我不認爲會快捷多少。因爲鏈表也不能直接找到要刪除的節點,只能根據第一個節點和最後一個節點來找

源碼
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    
        Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
    
        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;
    }

源碼說明:

  1. 調用remove(int index) 刪除
  2. 程序或根據node(int index) 找到要刪除的節點。這塊我認爲數據量大的時候也是很耗時的。
    先根據集合大小和查找對象的位置決定是從最後一個開始比對還是從第一個節點開始比對。最終獲取要刪除的節點
  3. unlink(Node x) 刪除節點方法。根據x節點的信息,取出上一個節點(prev)和下一個節點(prev)。將上一個節點保存的下一個節點修改爲x節點的下一個節點,將下一個節點的上一個節點修改爲x節點的上一個節點,最後刪除此節點

4. 修改數據 也要執行node(int index) 找到要修改的節點,然後直接修改此節點的item信息

5. 查詢會比ArrayList慢很多了。也是因爲執行node(int index) 在數據量大時比較耗時

6. LinkedList也是有序的

總結 ArrayList 和 LinkedList 的區別

  1. ArrayList是基於數組實現的,LinkedList是基於鏈表實現的
  2. 在ArrayList中查找和修改數據會比LinkedList中快很多
  3. LinkedList新增數據會比ArrayList快很多
  4. 對於刪除我覺得兩個都是很費時的。ArrayList刪除時,是將刪除元素之後的數據,複製進原數組從刪除元素開始的位置,在將列表最後一個元素設爲null。而LinkedList因爲不能直接定位到要刪除的節點。所以也是比較費時。

Vector

Vector也是一個類似於ArrayList的是的可變長度的數組類型,它的內部也是使用數組來存放數據對象的。
但不同於ArrayList的是Vector類中的操作集合的方法基本都是synchronized關鍵字修飾的。也就是線程安全的

    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    public synchronized E set(int index, E element) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
    .
    .
    .

List遍歷

        List<String> list = new ArrayList<>();
        list.add("sss");
        list.add("ddd");

        // 第一種(jdk1.8)
        list.forEach(str -> {
            System.out.println(str);
        });

        // 第二種
        for (String s : list) {
            System.out.println(s);
        }

        // 第三種
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章