JAVA集合-ArrayList

 JDK1.8,代碼:java.util.ArrayList.java

ArrayList

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

ArrayList實現了List、RandomAccess接口。可以插入空數據,也支持隨機訪問。

 

/**
 * 序列化的版本號
 */
private static final long serialVersionUID = 8683452581122892189L;

/**
 * 默認初始容量10
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 用於空實例的共享空數組實例
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 用於默認大小的空實例的共享空數組實例
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存儲ArrayList元素的數組緩衝區
 */
transient Object[] elementData; 

/**
 * ArrayList的大小
 */
private int size;

構造函數

/**
 * 用初始容量作爲參數的構造方法
 */
public ArrayList(int initialCapacity) {
	if (initialCapacity > 0) {
		//初始容量大於0,實例化數組
		this.elementData = new Object[initialCapacity];
	} else if (initialCapacity == 0) {
		//初始容量等於0,賦予空數組
		this.elementData = EMPTY_ELEMENTDATA;
	} else {
		throw new IllegalArgumentException("Illegal Capacity: "+
										   initialCapacity);
	}
}

/**
 * 無參的構造方法
 */
public ArrayList() {
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * 構造一個包含指定元素的列表集合
 * 按照集合返回它們的順序迭代器
 *
 * 要將其元素放入此列表的集合;如果指定的集合爲空,拋出NullPointerException
 */
public ArrayList(Collection<? extends E> c) {
	elementData = c.toArray(); // c爲空會拋出NullPointerException
	if ((size = elementData.length) != 0) {
		if (elementData.getClass() != Object[].class)
			elementData = Arrays.copyOf(elementData, size, Object[].class);
	} else {
		// 用空數組替換
		this.elementData = EMPTY_ELEMENTDATA;
	}
}

構造方法指定元素列表集合示例:

package com.container.arraylist;

import java.util.ArrayList;

/**
 * @Classname ArrayListDemo
 * @Description 構造方法指定元素列表集合示例
 * @Date 2019/10/19 13:01
 * @Created by xiangty
 */
public class ArrayListDemo {

    public static void main(String[] args) {
        ArrayList<A> arrayList = new ArrayList<A>();

        A a = null;
        for (int i = 0; i < 10; i++) {
            a = new A();
            a.setA("addValue" + i);
            arrayList.add(a);
        }

        ArrayList arrayList2 = new ArrayList(arrayList);
        // 打印arrayList2集合的值
        print(arrayList2);
    }

    public static void demo1() {
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        for (int i = 0; i < 10; i++) {
            arrayList.add(i);
        }

        ArrayList arrayList2 = new ArrayList(arrayList);
        // 打印arrayList2集合的值
        print(arrayList2);
    }

    public static <E> void print(ArrayList<E> arrayList){
        arrayList.forEach((a) -> System.out.println(a));
    }

}

class A {
    private String A;

    public String getA() {
        return A;
    }

    public void setA(String a) {
        A = a;
    }

    @Override
    public String toString() {
        return "A{" +
                "A='" + A + '\'' +
                '}';
    }

}

從上述的構造方法中可知,默認情況下elementData是一個大小爲0的空數組,當指定大小的時候,elementData的初始化大小變成了指定的初始化。

ArrayList擴容

ArrayList相當於動態數據,其中最重要的兩個屬性:elementData 數組和 size 大小。

add(E e)新增方法

/**
 * 將指定的元素追加到此列表的末尾
 */
public boolean add(E e) {
	ensureCapacityInternal(size + 1);  // Increments modCount!!
	elementData[size++] = e;
	return true;
}

首先進行擴容校驗

將插入的值放到尾部,並將 size + 1 

add(int index, E element)指定位置添加

public void add(int index, E element) {
	rangeCheckForAdd(index);

	ensureCapacityInternal(size + 1);  // Increments modCount!!
	//複製,向後移動
	System.arraycopy(elementData, index, elementData, index + 1,
					 size - index);
	elementData[index] = element;
	size++;
}

也是首先擴容校驗

接着對數據進行復制,目的是把 index 位置空出來放本次插入的數據,並將後面的數據向後移動一個位置

ensureCapacityInternal方法

/**
 * 確保內部容量
 */
private void ensureCapacityInternal(int minCapacity) {
	//先判斷數組是否爲空
	if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
		//如果數組爲空,將DEFAULT_CAPACITY(爲10)和minCapacity中較大的一個賦值給minCapacity
		minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
	}
	ensureExplicitCapacity(minCapacity);
}

a).當空列表add()第1個元素時,調試可知:原本minCapacity爲1,在Math.max()方法比較後,minCapacity爲10。

b).但當add()第2個元素時,數組非空,直接進入ensureExplicitCapacity(minCapacity)(此時minCapacity爲2)。

ensureExplicitCapacity方法

private void ensureExplicitCapacity(int minCapacity) {
	//定義於ArrayList的父類AbstractList,用於存儲結構修改次數
	modCount++;

	// overflow-conscious code
	if (minCapacity - elementData.length > 0)
		//如果數組的長度(elementData.length)小於最小容量(minCapacity),就擴容
		grow(minCapacity);
}

a).當要add第1個元素時,這時elementData.length(爲0,因爲此時還是空列表)是要比minCapacity(爲10)小的,會進入grow(minCapacity)方法(minCapacity爲10)

b).當add第2個元素時,調試可知:minCapacity爲2,此時elementData.length(即是容量)在添加第一個元素後擴容成10了,比minCapacity大,不會去執行grow方法 。數組容量仍爲10。

c).所以當添加第3、4···到第10個元素時,依然不會執行grow方法,數組容量都爲10。

d).直到添加第11個元素,minCapacity(爲11)比elementData.length(爲10)要大。進入grow方法進行擴容
 

grow方法進行擴容

/**
 * 用來分配數組的size最大值
 */
private void grow(int minCapacity) {
	// overflow-conscious code
	int oldCapacity = elementData.length;
	/*
		右移一位相當於原數除以2,位運算的計算方式相對除以計算更快  
		新容量擴大爲原來容量的1.5
	*/
	int newCapacity = oldCapacity + (oldCapacity >> 1);
	if (newCapacity - minCapacity < 0)
		//從minCapacity和這個newCapacity中取較大值作爲擴容後的新數組長度(新容量)。
		newCapacity = minCapacity;
		
	if (newCapacity - MAX_ARRAY_SIZE > 0)
		//如果新容量大於數組的最大size,進入hugeCapacity方法
		newCapacity = hugeCapacity(minCapacity);
		
	//將原來數組的數據複製到新的數組中
	elementData = Arrays.copyOf(elementData, newCapacity);
}

當add第1個元素時,oldCapacity爲0,經比較後第一個if判斷成立,newCapacity = minCapacity(爲10)。但是第二個if判斷不會成立,即newCapacity 不比 MAX_ARRAY_SIZE大,則不會進入hugeCapacity方法。數組容量爲10,add方法中return true,size增爲1

第3、4···到第10個元素邏輯同第1個

當add第11個元素時,newCapacity爲15,比minCapacity(爲11)大,第一個if判斷不成立。新容量沒有大於數組最大size,不會進入hugeCapacity方法。數組容量擴爲15,add方法中return true,size增爲11法最後一步,在確定好新數組的大小後,會調用Arrays.copyOf()方法,以適當長度(newCapacity)新建一個原數組的拷貝,並修改原數組,指向這個新建數組

 

grow()方法最後一步,在確定好新數組的大小後,會調用Arrays.copyOf()方法,以適當長度(newCapacity)新建一個原數組的拷貝,並修改原數組,指向這個新建數組。java垃圾回收機制會自動回收原數組

elementData = Arrays.copyOf(elementData, newCapacity);

hugeCapacity方法

private static int hugeCapacity(int minCapacity) {
	if (minCapacity < 0) // overflow
		throw new OutOfMemoryError();
		
	//對minCapacity和MAX_ARRAY_SIZE進行比較
	//若minCapacity大,將Integer.MAX_VALUE作爲新數組的大小
	//若MAX_ARRAY_SIZE大,將MAX_ARRAY_SIZE作爲新數組的大小
	return (minCapacity > MAX_ARRAY_SIZE) ?
		Integer.MAX_VALUE :
		MAX_ARRAY_SIZE;
}

ensureCapacity方法

add方法添加大量數據的時候,爲減少分配次數和提高效率,推薦使用ensureCapacity方法

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

 

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