1. 程式人生 > 實用技巧 >JavaEE - 11集合Collection-List

JavaEE - 11集合Collection-List

Javaee - 11集合Collection-List

(4)Collection子介面:List介面

(4.1)List介面概述

  • 鑑於陣列儲存資料的侷限性,通常使用List替代陣列。List-> "動態"陣列。
  • List集合類中元素有序、可重複,集合中的每個元素都有其對應的順序索引
  • List容器中的元素都對應一個整數型的序號記載其在容器中的位置,可以根據序號存取容器中的元素。
  • JDK API中List介面的實現類有: ArrayList、 LinkedList和 Vector。三者異同。
    • ArrayList: List介面的主要實現類;執行緒不安全,效率高;底層使用Object[]elementData儲存
    • LinkedList:對於頻繁插入刪除操作,效率比ArrayList高;底層使用雙向列表儲存
    • Vector: List介面的古老實現類;執行緒安全,效率低;底層使用Object[] elementData儲存

(4.2)ArrayList

(4.2.1)JDK 7下原始碼分析

  • ArrayList list = new ArrayList(); //底層建立初始長度為10的Object[]陣列elementData
  • list.add(123); // elementData[0] = new Integer(123);
  • ......
  • list.add(11); //如果此次新增導致底層elementData陣列容量不夠,則擴容
    預設情況下,擴容為原來的容量的1.5倍,同時將原有陣列中的資料複製到新的陣列中
  • 建議:開發中使用帶引數的構造器: ArrayList list = new ArrayList(int capacity); 節省擴容的效能開支

(4.2.2)JDK 8中的變化

  • ArrayList list = new ArrayList(); //底層Object[]陣列elementData初始化為{},不建立長度10的陣列
  • list.add(123); //第一次呼叫add時,底層建立長度10的陣列,新增元素elementData[0] = new Integer(123);
  • 後續的新增和擴容操作與JDK7一樣。
  • JDK7物件的建立類似於單例的餓漢式,JDK8的ArrayList物件建立類似於單例的懶漢式,延遲了陣列建立,節省記憶體。

(4.2.3)重要原始碼(JDK8)

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    public ArrayList() {
        // 不帶引數,初始化為空陣列
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 帶引數初始化
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            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)  // MAX_ARRAY_SIZE = Integer.MAX_VALUE -8
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

(4.3)LinkedList

  • linkedList 是一個雙向連結串列,內部沒有宣告陣列,而是定義了Node型別的first和last,用於記錄首末元素。
  • 定義內部類Node,作為LinkedList中儲存資料的基本結構。
  • Node除了儲存資料,還定義了兩個變數:prev變數記錄前一個元素的位置,next變數記錄下一個元素的位置
  • 沒有初始化大小,也沒有擴容的機制,就是一直在前面或者後面新增Node。

對於雙向連結串列Node的理解

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

在列表尾部插入一個元素

  • 先將列表最後一個元素last儲存為Node節點 l;
  • 組裝一個新的Node節點newNode, Node的prev是l,值為e, next為null。
  • 將新的節點newNode作為列表的最後一個元素。
  • 將原來元素的next指向 newNode新節點。如果是第一個元素,則把first也指向新節點。
  • 列表size加1,modCount加1。
    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++;
    }

    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

(4.4)Vector

JDK7和 JDK8中通過Vector()構造器建立物件時,底層都建立了長度為10的陣列。在擴容方面,預設擴容為原來的陣列長度的2倍。

    public Vector() {
        this(10);
    }

    public synchronized void ensureCapacity(int minCapacity) {
        if (minCapacity > 0) {
            modCount++;
            ensureCapacityHelper(minCapacity);
        }
    }

    private void ensureCapacityHelper(int minCapacity) {
        // 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 + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

(4.5)List介面

除了繼承Collection的方法,List添加了一些根據索引來操作集合元素的方法。

  • void add(int index, Object ele): 在index位置插入ele元素
  • boolean addAll(int index, Collection eles):從index位置開始將eles中的所有元素新增進去。
  • Object get(int index): 獲取指定index位置的元素
  • int indexOf(Object obj): 返回obj在集合中首次出現的位置
  • int lastIndexOf(Object obj): 返回obj在當前集合中末次出現的位置
  • Object remove(int index): 移除指定index位置的元素,並返回此元素
  • Object set(int index, Object ele): 設定指定index位置的元素為ele
  • List subList(int fromIndex, int toIndex): 返回從fromIndex到toIndex位置的子集合。

    @Test
    public void test1(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add("acd");
        list.add(new Person("Tom",12));

        System.out.println(list);                 //[123, acd, Person@27dd1a]
        list.add(2,"dddd");
        System.out.println(list);                 //[123, acd, dddd, Person@27dd1a]
        System.out.println(list.get(2));          //dddd
        System.out.println(list.set(2,"DDDD"));   //dddd
        System.out.println(list);                 //[123, acd, DDDD, Person@27dd1a]
    }

區分List中的remove(int index)和remove(Object obj) // remove(2) remove(new Integer(2))

    @Test
    public void testListRemove(){
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        updateList(list);
        System.out.println(list); //[1,2]
    }

    private static void updateList(List list){
        list.remove(2);   // index位置
    }

   @Test
    public void testListRemove(){
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        updateList(list);
        System.out.println(list);  // [1,3]
    }

    private static void updateList(List list){
        list.remove(new Integer(2));   // 刪除元素為2
    }

3.HashMap

HashMap 初始化大小是 16 ,擴容因子預設0.75(可以指定初始化大小,和擴容因子)
擴容機制.(當前大小 和 當前容量 的比例超過了 擴容因子,就會擴容,擴容後大小為 一倍。例如:初始大小為 16 ,擴容因子 0.75 ,當容量為12的時候,比例已經是0.75 。觸發擴容,擴容後的大小為 32.)