本文結構:
1.介紹特點
2.基本方法
3.重點源碼分析
1.介紹特點
ArrayList:
是List的一個具體實作子類,是List接口的一個數組實作 (裡面必定維護了一個數組)。
預設初始容量10, 擴容機制1.5倍。(數組必然有初始容量和擴容機制)
有序。
允許null元素。
允許重複元素。
線程不安全。
2.基本方法
關鍵字 | 簡介 |
---|---|
| 增加 |
| 判斷是否存在 |
| 擷取指定位置的對象 |
| 擷取對象所處的位置 |
| 删除 |
| 替換 |
| 擷取大小 |
| 轉換為數組 |
| 把另一個容器所有對象都加進來 |
| 清空 |
3.重點源碼分析
源碼解析:
import java.util.Arrays;
import java.util.ConcurrentModificationException;
public class MyArrayList<E>{
private static final int DEFAULT_CAPACITY = 10;// 預設容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 最大容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA ={} ;//空數組
private Object[] elements;//底層維護的數組
private int size;//容器内對象的個數
private int modCount;//記錄ArrayList這個對象被修改的次數
//構造方法
public MyArrayList() {
this.elements = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//擷取清單存的資料量
public int getSize() {
return size;
}
/**
*增加内容
* @param e
* @return
*/
public boolean add(E e) {
//建立一個預設為10的數組,若小則擴容
ensureCapacityInternal(size+1);
//確定容量夠,則添加資料,并講資料大小加1.
elements[size]=e;
size++;
return true;
}
//該方法來確定數組的容量夠用,若為建立則建立數組并賦予預設值。
private void ensureCapacityInternal(int minCapacity) {
//若數組為空(還未添加資料)
if (elements==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
//則選出預設值和最小容量的最大值
minCapacity=Math.max(DEFAULT_CAPACITY,minCapacity);
//隻add方法其實沒必要比較,主要是addAll()這個方法需要比較
}
modCount++;//修改次數加一
//判斷一下是否需要擴容,若資料+1大于目前數組,則需要擴容
if(minCapacity-elements.length>0){
grow(minCapacity);//調用擴容方法
}
}
/**
* 具體的擴容方法
* @param minCapacity
*/
private void grow(int minCapacity){
int oldCapacity=elements.length;//擷取原始數組的長度
int newCapacity=oldCapacity+(oldCapacity>>1);//擴容 1+0.5 倍
//若擴容後的長度比所需要的最低長度還要小,則直接把擴容的長度更改為最低所需長度
if (newCapacity-minCapacity<0){
newCapacity=minCapacity;
}
//若擴容完的新長度比規定的最大容量還大,則要進一步判斷,并進一步修改數組大小
if (newCapacity-MAX_ARRAY_SIZE>0){
//若所需的容量竟然小于0,說明超過Int最大值,越界了,抛出異常
if (minCapacity<0){
throw new OutOfMemoryError();
}
//若所需的容量不超過Int最大值,則再判斷
if (minCapacity>MAX_ARRAY_SIZE){//若所需的大于數組要求的而小于Int最大值
newCapacity=Integer.MAX_VALUE;//直接指派Int最大值
}else{//否則隻有小于數組最大值這一種情況了,賦予數組最大值即可
newCapacity=MAX_ARRAY_SIZE;
}
}
elements = new Object[newCapacity];
}
/**
* 根據下标添加元素
* @param index 下标
* @param element 要替換的元素
*/
public void add(int index,E element){
//先檢查一下下标是否越界
rangeCheck(index);
//確定數組長度夠用
ensureCapacityInternal(size+1);
//這裡調用System裡面一個靜态的本地方法(效率更高),用于數組的複制。這裡把下标後面的都向後移了。
/* public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
Object src:the source array. 源數組
int srcPos:starting position in the source array. 在源數組中,開始複制的位置
Object dest:the destination array. 目标數組
int destPos:starting position in the destination data. 在目标數組中,開始指派的位置
int length:the number of array elements to be copied. 被複制的數組元素的數量*/
System.arraycopy(element, index, element, index + 1, size - index);//等于說把下标後面的都向後移了。
//原數組 開始的位置 目标數組 目标數組中開始複制的位置 被複制的數量
//由于下标後的都往後移動了一位,是以index的位置為空,插進去即可
elements[index]=element;
size++;
}
/**
* 檢查下标是否越界
* @param index
*/
private void rangeCheck(int index) {
if (index>size||index<0){//若插入的下标比資料實際數量大或者輸入的是個不合常理的負數
throw new IndexOutOfBoundsException((outOfBoundsMsg(index)));//抛出異常并傳回要插入的下标數和實際資料的數量
}
}
/**
* 用于越界異常的資訊抛出
* @param index 輸入下标
* @return 傳回下标和實際資料的數量大小
*/
private String outOfBoundsMsg(int index) {
return "Index:"+index+",Size:"+size;
}
/**
* 清空清單所有元素
* 由垃圾回收器(GC)進行回收
*/
public void clear(){
//記錄對象修改次數
modCount++;
//讓數組所有的資料都變為null, clear to let GC do its work
for (int i=0;i<size;i++){
elements[i]=null;
}
//這時長度都為0了
size=0;
}
/**
* 根據下标更改資料
* @param index 下标
* @param element 想要更改的資料
* @return 傳回原始資料
*/
public E set(int index, E element) {
//首先判斷是否越界
rangeCheck(index);
E oldValue= (E) elements[index];
elements[index]=element;
return oldValue;
}
/**
* 檢查操作數是否一緻,防止并發修改錯誤
*//*
private void checkForComodification() {
if (MyArrayList.this.modCount != this.modCount)//線程中的和儲存的資料不一樣
throw new ConcurrentModificationException();//不一緻則抛出異常
}*/
/**
* 簡簡單單的根據下标查詢資料操作
* @param index 下标
* @return 傳回資料資訊
*/
public E get(int index) {
rangeCheck(index);
return (E) elements[index];
}
/**
* 根據下标删除元素
* @param index 要删除的下标
* @return 傳回要删除的資料
*/
public E remove (int index){
rangeCheck(index);//檢查一下下标是否合适
modCount++;//操作數加一
E oldValue= (E) elements[index];
//定義numMoved,記錄一下index後面的數,等會要把他們都移動了
int numMoved=size-index-1;
if (numMoved>0){
System.arraycopy(elements,index+1,elements,index,numMoved);//通過本地靜态的複制方法,來實作資料的前移。
}
elements[size]=null;//都前移了以後原本最後一位數的記憶體設為null,讓GC回收了 clear to let GC do its work
size--;
return oldValue;
}
/**
* 找到相應的元素并對其進行删除
* @param o
* @return 傳回true表示删除完成,false表示删除失敗
*/
public boolean remove(Object o) {
if (o == null) {//删除null資料
for (int index = 0; index < size; index++)
if (elements[index] == null) {
fastRemove(index);
return true;
}
} else {//删除正常的資料
for (int index = 0; index < size; index++)
if (o.equals(elements[index])) {
fastRemove(index);
return true;
}
}
return false;//若沒找到該資料則傳回false
}
/**
* 無需進行判斷下标是否合适的快速删除方法
* @param index
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elements, index+1, elements, index,
numMoved);
elements[--size] = null; // clear to let GC do its work
}
/**
* 判斷資料是否為空(清單裡沒資料)
* @return 若為空則傳回true
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 查詢是否有該資料
* @param o
* @return 若有傳回true
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;//若該索引為正,則表明有該資料,且第一次出現的位置在indexOf
}
/**
*傳回指定元素的第一次出現的索引
* @param o
* @return 若存在則傳回第一次出現的位置,若沒有則傳回-1
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elements[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}
/**
*傳回指定元素的最後一次出現的索引
* @param o
* @return 若存在則傳回最後一次出現的位置,若沒有則傳回-1
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)//從後向前周遊即可
if (elements[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elements[i]))
return i;
}
return -1;
}
@Override
public String toString() {
return "MyArrayList{" +
"elements=" + Arrays.toString(elements) +
'}';
}
}