【java】java集合類總結-------List

1.集合類

數組:可以存儲對象,也可以存儲基本數據類型,但是一次只能存儲一種類型,且長度一定,不可改變。

集合:只能存儲對象,長度可變,可以存儲不同類型的對象。Java集合類主要有三種:set,list,map

其中,實現邊框的是實現類,折線邊框的是抽象類,點線邊框的是接口

從圖中可以看出,Collection接口是集合類(List,Set,Queue)的根接口,java中沒有提供這個接口的直接實現類。有三個子接口List,Set,Queue,注意,Map不是collection的子接口。

Collection中的方法:

2.Collection中的List和Set接口

首先說一下List接口。 List裏存放的對象是有序的,可重複的,可以爲null的集合。List關注的是索引,擁有一系列和索引相關的方法,查詢速度快。

List接口下主要的三個實現類:Arraylist,Linkedlist,Vector。

(1)ArrayList

  arraylist實現List接口,繼承AbstractList。底層是數組實現,可以自增擴容。是非線程安全的,一般用於單線程環境中(與Vector最大的區別就是,V是線程安全的,所以A比V的性能相對要好些),在多線程中,可以選擇Vector或者CopyOnWriteArrayList。Arraylist實現了Serializable接口,支持序列化,能夠通過序列化傳輸;實現了RandomAccess接口(只是個標註接口,沒有實際的方法),支持快速隨機訪問,主要變現爲可以通過下標直接訪問(因爲Arraylist的底層是數組,可直接用數組下標來索引);實現了Cloneable接口,能被克隆。 Arraylist是基於動態數組實現的。

public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

  1)初始化

  Arraylist提供了三種初始化方法

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

    /**
     * Constructs an empty list with an initial capacity of ten.//默認提供容量爲10的數組。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class); //注意size是記錄該list集合當前元素的數量,不是容量
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

 

複製代碼

2)動態調整

無參構造函數默認的是空數組,爲什麼註釋說是容量爲10的數組。主要是ArrayList的add方法。add方法中調用了ensureCapacityInternal()方法,

 /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    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;
    }
 

從上述源碼中可以看出,當elementData爲空數組時,則使用Math.max(DEFAULT_CAPACITY, minCapacity)進行選擇一個最大的,其中DEFAULT_CAPACITY爲arraylist定義的靜態常量=10;

 

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

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    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);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

 

動態擴容最關鍵是grow方法 。通過源碼中int newCapacity = oldCapacity + (oldCapacity >> 1);可得容量擴大爲原來的1.5倍。

總之,ArrayList默認容量是10,如果初始化時一開始指定了容量,或者通過集合作爲元素,則容量爲指定的大小或參數集合的大小。每次擴容爲原來的1.5倍,如果新增後超過這個容量,則容量爲新增後所需的最小容量。如果增加1.5倍後的新容量超過限制的容量,則用所需的最小容量與限制的容量進行判斷,超過則指定爲Integer的最大值,否則指定爲限制容量大小。然後通過數組的複製將原數據複製到一個更大(新的容量大小)的數組。

3)遍歷方式

第一,隨機訪問,通過索引獲取元素。ArrayList實現了randomaccess接口。

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

第二,for循環,foreach循環。

package Two;

import java.util.ArrayList;

public class one {


    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList();
        arrayList.add(1);
        arrayList.add(3);
        arrayList.add(9);
    //for循環
        for (int i = 0;i<arrayList.size();i++){
            System.out.print(arrayList.get(i));
        }
//  foreach循環
        for (Integer list:arrayList) {
            System.out.print(list);
        }
    }
}

 

 

第三種:通過迭代器遍歷

 

public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList();
        arrayList.add(1);
        arrayList.add(3);
        arrayList.add(9);
        Integer integer = null;
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()){
            integer = (Integer) iterator.next();
            System.out.println(integer);
        }

    }

 

上述三種遍歷方式中,隨機訪問的效率最高,使用迭代器的效率最低。

 總結:

  • ArrayList是List接口的一個可變大小的數組的實現

  • ArrayList的內部是使用一個Object對象數組來存儲元素的

  • 初始化ArrayList的時候,可以指定初始化容量的大小,如果不指定,就會使用默認大小,爲10

  • 當添加一個新元素的時候,首先會檢查容量是否足夠添加這個元素,如果夠就直接添加,如果不夠就進行擴容,擴容爲原數組容量的1.5倍

  • 當刪除一個元素的時候,會將數組右邊的元素全部左移,添加一個元素時,右移。


     

2)LinkedList

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

LinkedList是一個繼承AbstractSequentialList的雙向鏈表。它可以被當作堆棧、隊列或雙端隊列進行操作。

LinkedList實現了List接口,能對它進行隊列操作。

LinkedList實現了Deque接口,即能將LinkedList當作雙端隊列使用

LinkedList實現了Cloneable接口,能克隆

LinkedList實現了java.io.Serializable接口,支持序列化,能通過序列化去傳輸

LinkedList是非同步的。

請注意,此實現不同步。 如果多個線程同時訪問鏈接列表,並且至少有一個線程在結構上修改列表,則必須在外部進行同步。 (結構修改是添加或刪除一個或多個元素的任何操作;僅設置元素的值不是結構修改。)這通常通過在自然封裝列表的對象上進行同步來實現。 如果沒有這樣的對象存在,列表應該使用Collections.synchronizedList方法“包裝”。 這最好在創建時完成,以防止意外的不同步訪問列表:

  List list = Collections.synchronizedList(new LinkedList(...)); 

 源碼閱讀有興趣可自己去看

LinkedList特點:

  • 雙向鏈表實現,沒有固定容量,不需擴容
  • 元素是有序的,允許null值,輸入輸出順序一致
  • 所有指定位置的操作都是從頭開始遍歷的
  • 需要更多的內存,LinkedList每個節點中需要存儲前後節點的信息,佔用空間更多
  • 查找效率低,插入刪除效率高。

3)Vector

Vector非常類似ArrayList,但是Vector是同步的。由Vector創建的Iterator,雖然和ArrayList創建的Iterator是同一接口,但是,因爲Vector是同步的,當一個Iterator被創建而且正在被使用,另一個線程改變了Vector的狀態(例如,添加或刪除了一些元素),這時調用Iterator的方法時將拋出ConcurrentModificationException,因此必須捕獲該異常。

vector源碼分析:https://www.cnblogs.com/skywang12345/p/3308833.html


3.List實現類的各種比較

 

1)Vector和ArrayList

相同點:兩者都是基於存儲元素的數組來實現的,它們會在內存中開闢塊連續的空間來存儲,由於數據存儲是連續的,它們支持用序號(下標)來訪問元素,但是插入和刪除是要移動容器中的元素,所以執行較慢。兩者都有一個初始化的容量的大小,爲10;當裏面存儲的元素超過這個大小時,就會動態的進行擴容。Vector默認擴充爲原來的2倍,ArrayList默認擴充爲原來的1.5倍。

區別:二者最大的區別在與synchronization(同步)的使用。在ArrayList中沒有一個方法是同步的,而在Vector中,絕大部分方法都是同步的。所以Vector是線程安全的,而ArrayList不是線程安全的。由於Vector提供同步,所以性能上較低於ArrayList。

2)ArrayList和LinkedList

  • ArrayList是實現了基於動態數組的數據結構,而LinkedList是基於雙向鏈表的數據結構
  • 對於隨機訪問,ArrayList要優於LinkedList,因爲LinkedList要移動指針
  • 對於插入和刪除,LinkedList較佔優勢,ArrayList要移動數據。
  • ArrayList和LinkedList都是非線程安全的容器

在實際使用中,若對數據的主要操作爲索引或只在集合的末端增加、刪除元素,使用Arraylist和vector效率比較高;若對數據的操作主要爲指定位置的插入或刪除操作,使用Linkedlist效率比較高;當在多線程中使用容器時(即多個線程會同時訪問該容器,),選用vector較爲安全。

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