java容器-ArrayList

java集合Collection

一些實用的基本的東西,不會去分析UML圖。

下面列出一些主要包含關係,淡然集合還有queue隊列但不在此處研究

List:可以有重複元素的集合、有序(存入和取出的順序不一定相同)

  1. ArrayList:非線程安全、底層是數組的實現,所以方便查詢,增刪不方便
  2. LinkedList:非線程安全、底層是鏈表實現,所以增刪方便,查詢不方便
  3. Vector:幾乎和ArrayList一樣只不過是用來synchronized加鎖,所以效率低一點,但是線程安全

Set:不可以有重複元素的集合、無序

  1. HashSet:線程不安全,存取速度快。底層是以哈希表實現的。
  2. TreeSet:線程不安全,查詢速度快。底層是以紅黑樹實現的。
  3. LinkedHashSet:有序,線程不安全,內部是以LinkHashMap實現的。

ArrayList

基於jdk 1.8

常量

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    private static final long serialVersionUID = 8683452581122892189L;
    //默認的大小
    private static final int DEFAULT_CAPACITY = 10;
    //空對象
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //空對象使用默認構造器時使用
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //數據存放數組,不參與序列化
    transient Object[] elementData; // non-private to simplify nested class access
    //長度
    private int size;
    //最大的大小
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

構造函數

//帶初始大小的構造函數
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 ArrayList() {
	 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
 }
    public ArrayList(Collection<? extends E> c) {
    	//將collection對象轉換成數組,然後將數組的地址的賦給elementData。
        elementData = c.toArray();
        //更新size的值,同時判斷size的大小,如果是size等於0,直接將空對象賦給elementData
        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);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

add

public boolean add(E e) {
		//這個方法是確保有位子可以放數據
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //存放數據,並讓size自增
        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++;
        //判斷給定長度是否大於數據長度,如果給定容量大說明放不下,需要擴容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        //擴容大小爲本身大小+本身大小/2,也就是1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //判斷是否滿足現在的需求,否就直接擴容到現在需要的大小
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //判斷是否超出界限
        if (newCapacity - MAX_ARRAY_SIZE > 0)
        	//這個方法裏面判斷了是否大於int最大上限,如果是就取上限否則就是MAX_ARRAY_SIZE 
            newCapacity = hugeCapacity(minCapacity);
        //複製擴容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
public void add(int index, E element) {
		//這個方法只是判斷是否越界,越界就拋出異常
        rangeCheckForAdd(index);
		//和add方法一樣
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //直接複製index後面的數據給elementData,並在index存入數據
        //相當於index後面的數據整體後移
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

get

這個太簡單了只是驗證超界然後從數組裏取數據

 public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }

set

簡單的判斷越界,取原數據存新數據並返回原數據

public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

remove

remove有兩種一個根據位置 一種根據數據

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

在這裏插入圖片描述
根據對象刪除分成兩種情況一種是循環判斷刪除用於刪除null,另外一個就是循環equals判斷刪除,真實的刪除方法是fastRemove(int index);

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

真實的刪除方法和根據下標刪除差不多

private void fastRemove(int index) {
        modCount++;
        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
    }

clear

循環清空數據,size變爲0,注意容量不變

public void clear() {
        modCount++;
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }

addAll

兩個方法和add方法差不多只不過從添加一個數據變成了,添加多個

public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                    numMoved);
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

contains

本質就是循環遍歷數組看看元素是否存在

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

trimToSize

public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
                    ? EMPTY_ELEMENTDATA
                    //這個方法主要就是去除size後面的容量
                    : Arrays.copyOf(elementData, size);
        }
    }

到這裏ArrayList算是差不多瞭解析了,還有一些內部類,自己的序列化方式等沒解析,也不在這裏講了,有興趣的小夥伴可以自己去看看。

知道上面一些基本就夠用了

小結

  1. ArrayList基於數組方式實現,擴容上限int的最大值
  2. 添加元素時可能要擴容,刪除元素時不會減少容量(若希望減少容量,trimToSize()),刪除元素時,將刪除掉的位置元素置爲null,下次gc就會回收這些元素所佔的內存空間。
  3. 線程不安全
  4. add(int index, E element):添加元素到數組中指定位置的時候,需要將該位置及其後邊所有的元素都整塊向後複製一位,所以效率比add低
  5. remove(Object o)需要遍歷數組,效率比不需要遍歷數組的remove(int index)低
  6. 每次擴容默認是1.5倍,如果還不夠就是給定的長度
  7. 默認大小是10
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章