可以看到ArrayList 繼承了 AbstractList
實現List,RandomAccess,Cloneable,序列化接口
怎麼理解capacity和size
可以看到我們的初始容量爲15
但是它的size爲4
這就說明size指的是 實際存儲了多少。
elementData: Object[]類型的動態數組
變量
// 序列化ID
private static final long serialVersionUID = 8683452581122892189L;
/**
* 初始容量 10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空的元素數據 且是Obejct數組
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 默認容量 空的數組
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* elementData 裏面存放的 Object數組
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 實際大小
*/
private int size;
/**
* 數組最大 大小爲 2^31-9
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
構造函數
/**
* 指定初始容量
*/
public ArrayList(int initialCapacity) {
// 指定初始容量 > 0
if (initialCapacity > 0) {
// 創建初始容量大小的元素
this.elementData = new Object[initialCapacity];
// 指定容量大小爲0
} else if (initialCapacity == 0) {
// 空的數據
this.elementData = EMPTY_ELEMENTDATA;
// 否則拋出異常
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 無參構造器 數組中爲空
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 1)將collection對象轉換成數組,然後將數組的地址的賦給elementData。
* 2)更新size的值,同時判斷size的大小,如果是size等於0,直接將空對象EMPTY_ELEMENTDATA的地址賦給elementData
* 3)如果size的值大於0,則執行Arrays.copy方法,把collection對象的內容(可以理解爲深拷貝)copy到elementData中。
* 注意:this.elementData = arg0.toArray(); 這裏執行的簡單賦值時淺拷貝,所以要執行Arrays,copy 做深拷貝
*/
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);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
前面一直對.length的概念有點混淆
特定做了一個測試:
發現length就是數組new出來的大小
如果在其中沒有添加
那麼Object類型裏面 未添加元素的下表默認爲null
很多方法的形參裏面有minCapacity:
minCapacity:直譯過來就是最小容量,即指我們添加元素後,或者自我指定容量的最小容量值。
擴容
/**
* trim實際大小:修整 實際大小
* ArrayList的容量調整爲實際元素的大小
*/
public void trimToSize() {
// 記錄List結構的修改次數 用於fast-fail
modCount++
//實際大小 < elementData數組長度
if (size < elementData.length) {
// 實際大小爲0?
// 爲0 定義爲空的elementData數組
// 不爲0 我們就定義size長的elementData數組
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
/**
* 方法名:確定容量
* 判斷所需的ro
* Explicit : 明確的,清晰的
* minCapacity: 所需長度
* 總結:
* elementData數組裏面爲空 那麼就執行ensureExplicitCapacity方法
* 裏面不爲空 但所需的長度大於默認容量10 執行ensureExplicitCapacity方法
*
*/
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);
}
}
/**
* 方法名:計算容量
* 數組默認爲空 返回默認容量和我們所需容量中的最大值
* 數組不爲null 返回所需容量
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 確定容量的內部
* internal: 內部的
*
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 確定擴容?
* explicit:明確的,清晰的
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 所需容量 > 數組的長度 擴容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 真正的擴容
*/
private void grow(int minCapacity) {
// 舊容量
int oldCapacity = elementData.length;
// 新容量 = 舊容量 +舊容量*0.5 = 舊容量*1.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 新容量小於所需容量
// 新容量 = 所需容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新容量 》 最大數組大小
// 所需容量大於最大數組大小 新容量 = Integer的最大值
// 所需容量小於最大數組大小 新容量 = 最大數組大小
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);
}
/**
* huge:巨大的
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
添加元素
這個方法其實和上面的add類似,該方法可以按照元素的位置,指定位置插入元素,具體的執行邏輯如下:
1)確保數插入的位置小於等於當前數組長度,並且不小於0,否則拋出異常
2)確保數組已使用長度(size)加1之後足夠存下 下一個數據
3)修改次數(modCount)標識自增1,如果當前數組已使用長度(size)加1後的大於當前的數組長度,則調用grow方法,增長數組
4)grow方法會將當前數組的長度變爲原來容量的1.5倍。
5)確保有足夠的容量之後,使用System.arraycopy 將需要插入的位置(index)後面的元素統統往後移動一位。
6)將新的數據內容存放到數組的指定位置(index)上
/**
* 確認元素內部容量大小 所需容量爲size+1
* e 賦值給 數組後一個下標
* 返回true值
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
*
*/
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++;
}
ArrayList總結
底層數組實現,使用默認構造方法初始化出來的容量是10
擴容的長度是原長度的1.5倍
實現了RandomAccess接口,底層又是數組,get讀取元素性能很好
線程不安全,所有的方法均不是同步方法也沒有加鎖,因此多線程下慎用
順序添加很方便
刪除和插入需要複製數組 性能很差(可以使用LinkindList)
爲什麼ArrayList的elementData是用transient修飾的?
transient修飾的屬性意味着不會被序列化,也就是說在序列化ArrayList的時候,不序列化elementData。
爲什麼要這麼做呢?
elementData不總是滿的,每次都序列化,會浪費時間和空間
重寫了writeObject 保證序列化的時候雖然不序列化全部 但是有的元素都序列化
Int和Integer的區別
Integer是一個包裝類,而int是一個基礎數據類型
在Integer有最大值和最小值
// 二進制結果爲0
@Native public static final int MIN_VALUE = 0x80000000;
// 二進制結果爲2147483647 2^31 -1
@Native public static final int MAX_VALUE = 0x7fffffff;
==參考 原文:https://blog.csdn.net/fighterandknight/article/details/61240861 ==