接著Java集合框架學(xué)習(xí)---深入探究ArrayList源碼(一)繼續(xù)學(xué)習(xí)ArrayList源碼撇眯。
- ensureCapacity函數(shù)
//當(dāng)ArrayList不處于默認(rèn)狀態(tài)時(shí),才可拓展為大小小于DEFAULT_CAPACITY容量的數(shù)組
// 否則只有指定大小超過(guò)DEFAULT_CAPACITY時(shí)才進(jìn)行擴(kuò)展旷祸;
//這個(gè)方法是public,區(qū)別于ensureCapacityInternal讼昆,這個(gè)方法是在外部使用的托享;
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);
}
}
這個(gè)函數(shù)的作用主要是在當(dāng)ArrayList的容量不足以容納當(dāng)前元素時(shí)給它設(shè)置新的容量。即在有必要的時(shí)候增加ArrayList的容量以確保其能夠容納最小容量參數(shù)所指定的元素?cái)?shù)量浸赫。其實(shí)在ArrayList類(lèi)中還有幾個(gè)私有方法與ensureCapaCity方法相互調(diào)用與配合才實(shí)現(xiàn)了ensureCapaCity對(duì)外發(fā)布的功能:
//ArrayList內(nèi)部使用此方法來(lái)拓展容量
//但是假如說(shuō)處于默認(rèn)狀態(tài)闰围,拓展大小仍不能小于DEFAULT_CAPACITY
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//如果不處于默認(rèn)狀態(tài),則調(diào)用ensureExplicitCapacity進(jìn)行拓展
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/*將容量變?yōu)橹付ù笮〖认浚绻绯鼍蜁?huì)拋出OutOfMemoryError錯(cuò)誤
*但是通過(guò)使用grow方法可以打破MAX_VALUE的限制
*/
private void grow(int minCapacity) {
// 用oldCapacity保存原有的容量大小
int oldCapacity = elementData.length;
//將新的容量設(shè)置為原來(lái)容量的3/2羡榴,可以減少由于小幅度擴(kuò)展帶來(lái)的開(kāi)銷(xiāo)
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果newCapacity沒(méi)有達(dá)到指定大小,則將其設(shè)定為指定大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
/*如果newCapacity比MAX_ARRAY_SIZE(Integer.MAX_VALUE(2147483647)-8)
*還大运敢,則調(diào)用hugeCapacity方法給newCapacity重新賦值
*/
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 否則就直接將該ArrayList實(shí)例拓展
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;
}
- contains函數(shù)
public boolean contains(Object o)
如果此列表中包含指定的元素校仑,則返回 true。更確切地講传惠,當(dāng)且僅當(dāng)此列表包含至少一個(gè)滿(mǎn)足 (o==null ? e==null : o.equals(e))的元素 e時(shí)迄沫,則返回 true。
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
其實(shí)在contains內(nèi)部是通過(guò)調(diào)用了indexOf函數(shù)來(lái)實(shí)現(xiàn)功能:
public int indexOf(Object o) {
//如果傳入的對(duì)象為空卦方,則嘗試在數(shù)組中找到一個(gè)為空的元素
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
//否則就在數(shù)組中嘗試能否找到與其值相同的元素
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//否則就返回-1
return -1;
}
indexOf函數(shù)
public int indexOf(Object o)
返回此列表中首次出現(xiàn)的指定元素的索引邢滑,或如果此列表不包含元素,則返回 -1(代碼見(jiàn)contains函數(shù)部分)愿汰。lastIndexOf函數(shù)
public int lastIndexOf(Object o)
返回此列表中最后一次出現(xiàn)的指定元素的索引困后,或如果此列表不包含索引,則返回 -1衬廷。更確切地講摇予,返回滿(mǎn)足(o==null ? get(i)==null : o.equals(get(i)))
的最高索引 i,如果不存在此類(lèi)索引吗跋,則返回 -1侧戴。
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
- clone函數(shù)
public Object clone()
返回此ArrayList實(shí)例的淺表副本,即對(duì)此ArrayList實(shí)例進(jìn)行淺層復(fù)制
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
- toArray函數(shù)
ArrayList中有兩個(gè)toArray函數(shù)
1.public Object[] toArray():
按適當(dāng)順序(從第一個(gè)到最后一個(gè)元素)返回包含此列表中所有元素的數(shù)組跌宛。由于該方法是分配一個(gè)新的數(shù)組酗宋,即此列表不維護(hù)對(duì)返回?cái)?shù)組的任何引用,因而它將是安全的疆拘,調(diào)用者可以自由的修改返回的數(shù)組蜕猫。需要注意的是,它通過(guò)調(diào)用Arrays中的copyOf函數(shù)來(lái)實(shí)現(xiàn)功能的哎迄。
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//Arrays中的copyOf函數(shù)
@SuppressWarnings("unchecked")
/*
*復(fù)制指定的數(shù)組回右,截取或用 null 填充(如有必要)隆圆,以使副本具有指定的長(zhǎng)度
*對(duì)于在原數(shù)組和副本中都有效的所有索引,這兩個(gè)數(shù)組將包含相同的值翔烁。對(duì)于在副本中有效而在原數(shù)組無(wú)效的所有索引渺氧,副本將包含 null。
*當(dāng)且僅當(dāng)指定長(zhǎng)度大于原數(shù)組的長(zhǎng)度時(shí)蹬屹,這些索引存在侣背。所得數(shù)組和原數(shù)組屬于完全相同的類(lèi)。
*/
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
//被上面的 public static <T> T[] copyOf方法調(diào)用
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
//如果該數(shù)組存儲(chǔ)的是Object類(lèi)型的元素慨默,就New一個(gè)Object類(lèi)型的數(shù)組贩耐,否則就使用newInstance產(chǎn)生一個(gè)對(duì)應(yīng)類(lèi)型的數(shù)組
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
//Array中的public static Object newInstance(Class<?> componentType, int length)函數(shù)
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
//
2.public <T> T[] toArray(T[] a):
按適當(dāng)順序(從第一個(gè)到最后一個(gè)元素)返回包含此列表中所有元素的數(shù)組;返回?cái)?shù)組的運(yùn)行時(shí)類(lèi)型是指定數(shù)組的運(yùn)行時(shí)類(lèi)型业筏。如果指定的數(shù)組能容納列表,則將該列表返回此處鸟赫。否則蒜胖,將分配一個(gè)具有指定數(shù)組的運(yùn)行時(shí)類(lèi)型和此列表大小的新數(shù)組。如果指定的數(shù)組能容納隊(duì)列抛蚤,并有剩余的空間(即數(shù)組的元素比隊(duì)列多)台谢,那么會(huì)將數(shù)組中緊接 collection 尾部的元素設(shè)置為 null(僅 在調(diào)用者知道列表中不包含任何 null 元素時(shí)才能用此方法確定列表長(zhǎng)度)。
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
//size為該ArrayList的size
if (a.length < size)
// 必須創(chuàng)建一個(gè)與參數(shù)類(lèi)型相同的數(shù)組
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;//用來(lái)幫助調(diào)用者確定集合長(zhǎng)度(只有在明確知道集合中沒(méi)有null元素時(shí)才有用)
return a;
}
- elementData方法(缺省方法)
E elementData(int index):
位置訪問(wèn)操作岁经,返回列表中下標(biāo)為index的元素的值
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
- get方法
public E get(int index):
返回此列表中指定位置上的元素(調(diào)用了上面的elementData方法).
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
可以看到朋沮,get方法內(nèi)部也調(diào)用了一個(gè)rangeCheck,我們來(lái)看看這個(gè)方法:
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
這個(gè)rangeCheck方法主要是用來(lái)進(jìn)行越界判斷的:如果傳入的index比列表的size大缀壤,那么就拋出一個(gè)IndexOutOfBoundsException錯(cuò)誤樊拓。
好奇......如果index<0的話是怎么進(jìn)行數(shù)組越界判斷的呢?塘慕?我還沒(méi)找到原因筋夏,先挖個(gè)坑,等找到了方法再填坑。
- set方法
public E set(int index, E element):
用指定的元素替代此列表中指定位置上的元素(返回值為被替代的元素的值)图呢。
public E set(int index, E element) {
//進(jìn)行越界判斷条篷,看看index是否大于size
rangeCheck(index);
//取出被替代的值
E oldValue = elementData(index);
//將指定元素替代此列表中指定位置上的元素
elementData[index] = element;
//返回指定位置上被替代的元素
return oldValue;
}
- add方法
1.public boolean add(E e):
將指定的元素添加到此列表的尾部(添加成功則返回true).
public boolean add(E e) {
ensureCapacityInternal(size + 1); // modCount加一
elementData[size++] = e;
return true;
}
其實(shí)在往列表尾部添加元素的時(shí)候要保證列表還有空間存放元素,ensureCapacityInternal函數(shù)就是用來(lái)完成此
工作的蛤织,關(guān)于ensureCapacityInternal具體的內(nèi)部實(shí)現(xiàn)參見(jiàn)上方ensureCapacity函數(shù)部分赴叹。
2.public void add(int index, E element):
將指定的元素插入此列表中的指定位置。向右移動(dòng)當(dāng)前位于該位置的元素(如果有)以及所有后續(xù)元素(將其索引加 1)指蚜。
public void add(int index, E element) {
//判斷是否越界
rangeCheckForAdd(index);
//由于對(duì)列表結(jié)構(gòu)進(jìn)行了修改乞巧,所以必須增加modCount,關(guān)于ensureCapacityInternal參見(jiàn)上方ensureCapacity函數(shù)部分摊鸡。
ensureCapacityInternal(size + 1);
//將從Index的元素都往后移一個(gè)位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//在指定的index位置插入需要添加的值
elementData[index] = element;
//列表的size加一
size++;
}
add函數(shù)內(nèi)部還調(diào)用了rangeCheckForAdd函數(shù)摊欠,作用是判斷指定的位置是否越界丢烘。下面是rangeCheckForAdd函數(shù)源碼:
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
- remove函數(shù)
1.* public E remove(int index):*
移除此列表中指定位置上的元素。向左移動(dòng)所有后續(xù)元素(將其索引減 1)些椒,返回值為被移出的元素播瞳。
public E remove(int index) {
//檢查下標(biāo)是否越界
rangeCheck(index);
//由于對(duì)列表結(jié)構(gòu)進(jìn)行了修改,modCount++
modCount++;
//保存被移出的值
E oldValue = elementData(index);
//計(jì)算有多少元素需要向左移動(dòng)
int numMoved = size - index - 1;
if (numMoved > 0)
//將Index后面的元素向左移動(dòng)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 將第size個(gè)元素賦值為null以觸發(fā)垃圾回收免糕,并且size減一
elementData[--size] = null;
//返回被移出的值
return oldValue;
}
2.* public boolean remove(Object o):*
移除此列表中首次出現(xiàn)的指定元素(如果存在)赢乓。如果列表不包含此元素,則列表不做改動(dòng)石窑。更確切地講牌芋,移除滿(mǎn)足 (o==null ? get(i)==null : o.equals(get(i)))的最低索引的元素(如果存在此類(lèi)元素)。如果列表中包含指定的元素松逊,則返回 true(或者等同于這種情況:如果列表由于調(diào)用而發(fā)生更改躺屁,則返回 true)。
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;
}
其實(shí)此方法內(nèi)部主要還是靠調(diào)用fastRemove來(lái)完成移出功能的经宏,以下是fastRemove源碼(其實(shí)具體實(shí)現(xiàn)和public E remove(int index)函數(shù)差不多犀暑,只不過(guò)它不會(huì)返回被移出的元素的值):
private void fastRemove(int index) {
//由于對(duì)列表結(jié)構(gòu)進(jìn)行了修改,因此modeCount加一
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函數(shù)
public void clear() :
移除此列表中的所有元素烁兰。此調(diào)用返回后耐亏,列表將為空。
public void clear() {
modCount++;
// 移出所有元素并觸發(fā)垃圾回收
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
- addAll函數(shù)
1.public boolean addAll(Collection<? extends E> c):
按照指定 collection 的迭代器所返回的元素順序沪斟,將該 collection 中的所有元素添加到此列表的尾部广辰。如果正在進(jìn)行此操作時(shí)修改指定的 collection ,那么此操作的行為是不確定的主之。(這意味著如果指定的 collection 是此列表且此列表是非空的择吊,那么此調(diào)用的行為是不確定的)。
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;
}
2.public boolean addAll(int index, Collection<? extends E> c):
從指定的位置開(kāi)始槽奕,將指定 collection 中的所有元素插入到此列表中干发。向右移動(dòng)當(dāng)前位于該位置的元素(如果有)以及所有后續(xù)元素(增加其索引)。新元素將按照指定 collection 的迭代器所返回的元素順序出現(xiàn)在列表中史翘。
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;
}