Java集合框架概覽之ArrayList源碼刨析
一、從一段簡單的代碼入手
下面是一段簡單的集合操作代碼,實例化一個 ArrayList 集合并插入和獲取元素的代碼:
public static void main(String[] args) {
// 實例化一個初始容量為5的 ArrayList 集合
List list = new ArrayList<String>(6);
// 向指定索引位置插入數(shù)據(jù)
list.add(1, "hello");// 代碼行號:17
// 獲取指定索引位置的數(shù)據(jù)
System.out.println(list.get(1));
}小伙伴可以先思考一下執(zhí)行的結(jié)果是什么?
好啦,揭曉謎底:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:665)
at java.util.ArrayList.add(ArrayList.java:477)
at com.example.qgdemo.studydemo.Test.Test2.main(Test2.java:17)
細心的小伙伴已經(jīng)注意到了上面的那段代碼有一行專門標注了行號,而執(zhí)行的結(jié)果的異常行號剛好是我標注的那一行,不難得出 就是在:list.add(1, "hello");這一行就拋出了異常。那么問題到底出現(xiàn)在哪里了呢?
下面我們從這短短幾行代碼逐行深入源碼去刨析,挖出隱藏寶藏。
二、初始化
ArrayList的初始化
先從集合的初始化入手:
List list = new ArrayList<String>(5);
上源碼(硬菜):
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Constructs an empty list with the specified initial capacity.
* 根據(jù)指定的初始化容量構(gòu)造一個空的 list 集合
* @param initialCapacity the initial capacity of the list 初始化的容量
* @throws IllegalArgumentException if the specified initial capacity
* is negative 如果指定的容量為負數(shù)則拋出異常
*/
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);
}
}簡單分析一下這段源碼:
- ArrayList 底層采用普通的數(shù)組來存儲數(shù)據(jù),通過
elementData這一成員變量來存儲集合的數(shù)據(jù)。 - 在實例化是當傳入的參數(shù)大于零,則實例化一個對應(yīng)容量的 Object 數(shù)組并賦值給我們的
elementData成員變量。 - 在實例化是當傳入的參數(shù)等于零,安裝默認的
EMPTY_ELEMENTDATA空數(shù)組賦值給elementData 成員變量。 - 在實例化是當傳入的參數(shù)小于零,則拋出指定的 IllegalArgumentException 異常信息。
小貼士: 細心的小伙伴會注意到我們的 elementData成員變量使用了 transient 關(guān)鍵字修飾,這里簡單科普一下:
被 transient 修飾的變量不能被序列化。
transient 只能作用于實現(xiàn)了 Serializable 接口的類當中。
transient 只能用來修飾普通成員變量字段。
分析到這里目前沒有發(fā)現(xiàn)關(guān)于我們的問題的信息,我們繼續(xù)往下看。
三、添加元素
ArrayList添加元素
現(xiàn)在到了我們的重頭戲,從執(zhí)行結(jié)果反饋來看,拋出異常的位置就在這:list.add(1, "hello");,讓我們磨刀霍霍向源碼一探究竟。
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
* 向 ArrayList 中指定的位置插入指定的元素,如果當前位置已經(jīng)有元素,則會將該位置之后的所有元素統(tǒng)一往后移一位。
* @param index index at which the specified element is to be inserted 待插入的索引位置
* @param element element to be inserted 待插入的元素
* @throws IndexOutOfBoundsException {@inheritDoc} 下標越界異常
*/
public void add(int index, E element) {
rangeCheckForAdd(index); // 越界檢查
ensureCapacityInternal(size + 1); // Increments modCount!! 是否擴容的判斷
System.arraycopy(elementData, index, elementData, index + 1,
size - index); // 數(shù)組拷貝
elementData[index] = element; // 將待添加的元素放入指定的位置
size++; // 集合的實際大小累加
}
/**
* The size of the ArrayList (the number of elements it contains).
* ArrayList 的成員變量,保存了已有元素的數(shù)量
* @serial
*/
private int size;
/**
* A version of rangeCheck used by add and addAll.
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}源碼刨析:
- 首先看
rangeCheckForAdd(index);這一行,這個方法主要是檢查待插入的 index 索引是否越界或者非法。 經(jīng)過縝密分析(debug ),發(fā)現(xiàn)正是這里的檢查拋出的異常,導(dǎo)致我們出師未捷身先死/(ㄒoㄒ)/~~,第一步就被絆倒了。 - 既然判斷的是 index 和 size 的大小,那么我們回過頭看一下:
private int size;這個玩意,通過其注釋我們得知這個成員變量保存了已有元素的數(shù)量,那么問題就很明顯了:我們初始化后的集合雖然已經(jīng)有了一個指定容量的數(shù)組,但是并沒有實際元素,所以 size 依然為0。不難得出結(jié)論:==這種指定位置插入元素的方法必須從下標0開始順次插入元素,你敢隔空插入它就敢死給你看!==
好了,雖然問題的根源找到了,但是源碼我們還是要繼續(xù)往下看的。
// 判斷是否需要擴容
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 修改次數(shù)累加
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
* 增加集合的容量以確保容納至少
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);// 將原容量擴容至原來的1.5倍,以本例來說就是擴容至:6+3=9
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; //取 newCapacity 和 minCapacity 的最大值賦值給 newCapacity,考慮了溢出的情況
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);
}
/**
* The maximum size of array to allocate. 定義了數(shù)組允許分配的最大長度
* Some VMs reserve some header words in an array. 一些虛擬機在數(shù)列中會保留一些頭部信息(需要預(yù)留一定容量)
* Attempts to allocate larger arrays may result in 嘗試取分配更長的數(shù)組可能會導(dǎo)致內(nèi)存溢出
* OutOfMemoryError: Requested array size exceeds VM limit :申請的數(shù)組長度超過了虛擬機的限制
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow 溢出檢查
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? // 如果申請的最小容量比數(shù)組的容量上限還大則容量設(shè)置為:
Integer.MAX_VALUE : // Integer.MAX_VALUE,否則設(shè)置為:數(shù)組容量上限(MAX_ARRAY_SIZE)
MAX_ARRAY_SIZE;
}源碼刨析:
- 這一方法:
private void ensureCapacityInternal(int minCapacity)主要是為了檢查集合是否可以滿足指定的最小數(shù)量的元素的要求。 - 如果滿足的話,則只需要將修改次數(shù):
modCount++累加就完事。 - 如果容量不夠用了,則需要進行擴容,那么就需要調(diào)用
grow(int minCapacity)方法來執(zhí)行擴容任務(wù)。 - 通過
int newCapacity = oldCapacity + (oldCapacity >> 1);方法將原來的容量擴容1.5倍,后續(xù)的兩個 if 判斷考慮了 newCapacity 溢出的情況,最終保證了 newCapacity 必然為正數(shù)。
小貼士: 上面的:grow(int minCapacity)方法用到了移位運算符。 java中有三種移位運算符: << :左移運算符,num << 1,相當于num乘以2。 >> :右移運算符,num >> 1,相當于num除以2。 >>>:無符號右移,忽略符號位,空位都以0補齊。
確定了集合的新容量,接下來就需要將集合的舊數(shù)據(jù)拷貝到新數(shù)組當中:
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//[#System] 調(diào)用了系統(tǒng)級的數(shù)組拷貝方法
/**
* @param src the source array. 源數(shù)組
* @param srcPos starting position in the source array. 源數(shù)組的起始下標
* @param dest the destination array. 目標數(shù)組
* @param destPos starting position in the destination data. 目標數(shù)組的起始下標
* @param length the number of array elements to be copied. 需要拷貝的元素數(shù)量
* */
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
源碼刨析:
- 這塊內(nèi)容沒啥好說的,無非就是調(diào)用系統(tǒng)函數(shù)進行新舊數(shù)據(jù)的拷貝。
- 主要看看上面數(shù)組拷貝方法的注釋,做一個大致的了解。
四、ArrayList獲取元素
ArrayList 由于是基于數(shù)組來存儲數(shù)據(jù)的,所以支持按指定下標來獲取數(shù)據(jù):
/**
* Returns the element at the specified position in this list.
* 返回集合指定位置的元素
* @param index index of the element to return 要返回的元素下標
* @return the element at the specified position in this list 指定下標的元素
* @throws IndexOutOfBoundsException {@inheritDoc} 下標越界異常
*/
public E get(int index) {
rangeCheck(index); // 索引越界檢查
return elementData(index); // 按下標獲取元素
}
/**
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does *not* check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an ArrayIndexOutOfBoundsException if index is negative.
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}源碼刨析:
- 這塊內(nèi)容主要也就進行了一次下標越界的檢查,檢查通過就直接返回數(shù)據(jù)。
五、刪除元素
ArrayList 主要提供了:指定下標刪除,按元素刪除,批量刪除,特定條件刪除,下標區(qū)間刪除等方法。
5.1 指定下標刪除
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
* 刪除特定位置的集合元素,將該元素之后的所有元素往前挪一位。
* @param index the index of the element to be removed 待刪除的元素下標
* @return the element that was removed from the list 返回已刪除的元素
* @throws IndexOutOfBoundsException {@inheritDoc} 下標越界異常
*/
public E remove(int index) {
rangeCheck(index); // 下標越界檢查
modCount++; // 修改次數(shù)累加
E oldValue = elementData(index);
int numMoved = size - index - 1; // 計算需要移動的元素數(shù)量,指的就是當前刪除位置之后的元素數(shù)量
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved); // 重新進行數(shù)據(jù)拷貝
elementData[--size] = null; // clear to let GC do its work 將數(shù)組末尾空缺出來的位置引用置為null,便于GC
return oldValue;
}源碼刨析:
- 首先就是進行下標越界的檢查,然后就是修改次數(shù)的累加以及獲取待刪除的舊數(shù)據(jù)。
- 這塊:
int numMoved = size - index - 1;主要是計算一下當前待刪除元素之后有多少需要移動的元素數(shù)量。 - 這個
numMoved的值可能為 0 ,比如說當前集合就一個元素,在刪除下標為 0 的時候,numMoved的值就為 0 ,所以接下來做了一次 if 是否大于零的判斷,如果為 true,則執(zhí)行數(shù)組的拷貝,將需要處理的元素全部往前移動一位。 - 上一步移動完元素之后,數(shù)組的最后一個位置就空缺出來了,然后就通過
elementData[--size] = null;將該位置的引用置為 null 便于GC處理。
5.2 按元素刪除
/**
* Removes the first occurrence of the specified element from this list,
* 如果集合中指定的元素存在的話,刪除首次出現(xiàn)的那個指定元素。
* if it is present. If the list does not contain the element, it is
* 如果指定元素不存在,則不會有什么影響。
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* 一般情況下,會刪除下標最小的那個元素
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present 待刪除的元素
* @return <tt>true</tt> if this list contained the specified element 如果元素存在則返回true,反之為false
*/
public boolean remove(Object o) {
if (o == null) { // 如果待刪除的元素為 null,則直接遍歷數(shù)組元素和 null 進行匹配
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index); // 執(zhí)行刪除操作
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) { // 遍歷匹配所有元素
fastRemove(index);// 執(zhí)行刪除操作
return true;
}
}
return false;
}
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++; // 修改次數(shù)累加
int numMoved = size - index - 1;// 計算需要移動的元素數(shù)量,指的就是當前刪除位置之后的元素數(shù)量
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);// 重新進行數(shù)據(jù)拷貝
elementData[--size] = null; // clear to let GC do its work 將數(shù)組末尾空缺出來的位置引用置為null,便于GC
}源碼刨析:
- 首先根據(jù)待刪除的元素是否為
null進行分別處理。 - 如果待刪除的元素是
null的話,則遍歷所有數(shù)組元素和null進行匹配,匹配到第一個的話則執(zhí)行刪除,直接返回true。 - 如果不是,則遍歷所有數(shù)組元素和待刪除元素進行
equals匹配,匹配到第一個的話則執(zhí)行刪除,直接返回true。 - 對于
fastRemove這個方法的話,筆者認為在上一個指定下標刪除的時候可以直接調(diào)用這個方法的。
小貼士: 由上述源碼可以得出一個結(jié)論:==如果你想刪除一個 ArrayList 中的 所有 null 元素,調(diào)用一次 remove(null); 是無法刪除全部的null元素的。==
5.3 批量刪除
/**
* Removes from this list all of its elements that are contained in the
* specified collection.
* 按給定的特定元素集合去刪除當前集合的匹配元素。
* @param c collection containing elements to be removed from this list 包含待刪除元素的刪除
* @return {@code true} if this list changed as a result of the call
* @throws ClassCastException if the class of an element of this list
* is incompatible with the specified collection
* (<a href="Collection.html#optional-restrictions" rel="external nofollow" rel="external nofollow" >optional</a>)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
* (<a href="Collection.html#optional-restrictions" rel="external nofollow" rel="external nofollow" >optional</a>),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c); // 對 c 集合進行空判斷
return batchRemove(c, false); // 執(zhí)行批量刪除
}
// 批量刪除方法
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false; // 操作結(jié)果標識
try {
// 通過遍歷原集合,將不符合刪除條件的 [r] 位置的元素替換掉 [w] 位置的元素,并將 [w] 累加。
for (; r < size; r++) // 每次循環(huán) r++
if (c.contains(elementData[r]) == complement) // 如果待刪除的集合不包含有原集合的元素
elementData[w++] = elementData[r]; // 則用原集合當前下標位置 [r] 的元素覆蓋掉下標位置為 [w] 的元素
// 并將 [w] 累加。
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
// 只有在拋出異常時:r != size ,那么此時需要將未比對的元素拼接在已經(jīng)處理過的元素后面
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r; // 重新設(shè)置 [w] 的值,因為在下一步會將 [w] 之后的元素設(shè)置為null,此時的 [r] 為拋出異常位置
}
// 在極端情況下,如果待刪除集合和原集合的元素完全無交集,則 `w == size`,這種情況下無需對原集合進行任何操作。
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null; // 將 [w] 之后的元素全部賦值為 null。
modCount += size - w; // 對 modCount 進行重新設(shè)置
size = w;
modified = true;
}
}
return modified;
}
// 集合是否包含指定的元素
public boolean contains(Object o) {
return indexOf(o) >= 0; // 遍歷集合查找指定元素
}源碼刨析:
- 首先對傳入的參數(shù)集合進行
null判斷,如果為空則直接拋出異常。 - 接下來就是通過
batchRemove方法執(zhí)行批量刪除。 - 這個:
batchRemove方法首先會遍歷原集合,使用其 [r] 位置元素去匹配在待刪除集合是否存在: - 如果不存在,說明該元素并不是要刪除元素,則:
if (c.contains(elementData[r]) == complement)返回true,此時的原集合的 [r] 位置元素需要覆蓋到原集合的 [w] 位置,此時 [w] 進行累加,方便下次進行覆蓋。 - 在循環(huán)完畢后,最終的結(jié)果就是:原集合的 [w] 位置之前的元素都是需要保留下來的。
- 在
finally代碼塊中:進行的第一個if (r != size)判斷是為了在出現(xiàn)異常時(此時r != size)單獨將后續(xù)未處理完的 [r] 之后數(shù)據(jù)拷貝到原集合的 [r] 之后,保證數(shù)據(jù)的完整性。另外還需要重新計算 [w] 的值。 - 在這一步:
if (w != size)判斷是為了將 [w] 之后的重疊需要刪除的數(shù)據(jù)賦值為 null,最后修改 modCount 和集合的大小 size 的值。 - 總之按博主的理解,這個
batchRemove方法總體思想是:將原數(shù)組中不匹配的元素通過替換的方式往前聚集,處理到最后那么后面的那部分元素就可以廢棄掉了。
5.4 特定條件刪除
// 根據(jù)指定的過濾器判斷匹配的元素是否在集合內(nèi):Predicate 接口主要用來判斷一個參數(shù)是否符合要求。
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter); // 指定的過濾器的非null判斷
// figure out which elements are to be removed 找出要刪除的元素
// any exception thrown from the filter predicate at this stage 在這階段拋出的任何異常都不會使得集合發(fā)生改變
// will leave the collection unmodified
int removeCount = 0; // 刪除數(shù)目
final BitSet removeSet = new BitSet(size); // BitSet是一種特殊類型的數(shù)組來保存位值,其中數(shù)組大小會隨需要增加
final int expectedModCount = modCount; // 記錄修改次數(shù)
final int size = this.size;
// 這個循環(huán)主要是為了記錄需要刪除的元素數(shù)目
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i); // 通過 removeSet 來記錄需要刪除的集合下標
removeCount++; // 刪除數(shù)目進行累加
}
}
if (modCount != expectedModCount) { // 正常情況下這兩個值應(yīng)該是相等的,不相等說明有了并發(fā)修改,則拋出異常
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
// 通過遍歷使用未刪除的元素替換已刪除元素,[i] 代表未刪除的元素下標,[j] 代表被替換的元素下標
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount; // 記錄刪除后的新的容量
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i); // 找出未刪除元素的下標 [i]
elementData[j] = elementData[i]; // 使用未刪除的元素 [i] 替換對應(yīng)位置 [j] 的元素
}
for (int k=newSize; k < size; k++) { // 將下標從 [k] 到之后的位置的元素賦值為null
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) { // 出現(xiàn)并發(fā)修改時拋出異常
throw new ConcurrentModificationException();
}
modCount++; // 修改次數(shù)累加
}
return anyToRemove;
}源碼刨析:
- 這個方法
removeIf支持指定一個過濾器(Predicate)來刪除指定的若干元素。 - 在這個方法中用到了:
final BitSet removeSet = new BitSet(size);BitSet 是一種特殊類型的數(shù)組,它只能記錄兩種狀態(tài):0和1,可以用來代表有沒有,是與否等數(shù)據(jù)。在這塊是為了存儲需要刪除的元素的下標。 - 這里使用一個和集合的
size一樣容量的 BitSet 來對應(yīng)每一個集合元素的下標,方便后續(xù)處理。 - 通過
for (int i=0; modCount == expectedModCount && i < size; i++)這個循環(huán)匹配集合中需要刪除的元素,用其下標來為removeSet對應(yīng)位置 [i] 的狀態(tài)位的值為true。 - 到了
if (modCount != expectedModCount)這一步就是常規(guī)的并發(fā)修改的檢查,如果出現(xiàn)并發(fā)修改則直接拋出異常。 - 在
anyToRemove的值大于零,也就是匹配到有需要刪除的元素,則開始執(zhí)行數(shù)據(jù)的刪除操作。 - 通過
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++)這個循環(huán)來處理,利用BitSet的i = removeSet.nextClearBit(i);獲取得到下一個值為false的下標值,也就是獲取未被標記刪除的元素下標。 - 進行數(shù)據(jù)替換:
elementData[j] = elementData[i];,上一步已經(jīng)獲取到未被標記刪除的元素下標 [i],在這一步就可以順次替換掉 [j] 位置的元素,這樣一來就能保證未被標記刪除的元素最終都集中在集合前面連續(xù)部分的位置,也就是在下標[newSize]之前。 - 通過這部分:
for (int k=newSize; k < size; k++)進行遍歷 [k] 之后的元素,執(zhí)行elementData[k] = null;把下標 [k] 以及之后的位置元素賦值為null,便于下一次的 GC 。 - 最后再次進行并發(fā)修改的檢查以及集合的修改次數(shù)的累加。
小貼士: 對于這個方法使用這里給一個簡單的例子: 假設(shè)有一個字符串集合 list:["Google","Runoob","Taobao","Facebook"],我們想刪除所有帶有 ”oo“的元素; 則可以:list.removeIf(e -> e.contains("oo"));,最終的集合就變?yōu)椋篬"Taobao"]。
5.5 下標區(qū)間刪除
/**
* Removes from this list all of the elements whose index is between
* {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
* 刪除下標區(qū)間 [fromIndex]至[toIndex]之間的元素,包含[fromIndex] 但是不包含 [toIndex] 。
* Shifts any succeeding elements to the left (reduces their index).
* 將刪除區(qū)間之后的集合元素統(tǒng)一左移動。
* This call shortens the list by {@code (toIndex - fromIndex)} elements.
* (If {@code toIndex==fromIndex}, this operation has no effect.)
* 如果[fromIndex]和[toIndex]相等,則對集合無影響。
* @throws IndexOutOfBoundsException if {@code fromIndex} or
* {@code toIndex} is out of range
* ({@code fromIndex < 0 ||
* fromIndex >= size() ||
* toIndex > size() ||
* toIndex < fromIndex})
*/
protected void removeRange(int fromIndex, int toIndex) {
modCount++; // 修改次數(shù)累加
int numMoved = size - toIndex; // 需要移動的元素數(shù)量,也就是待刪除區(qū)間的末尾之后的元素數(shù)量。
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved); // 用刪除區(qū)間的末尾之后的元素拷貝至刪除區(qū)間的元素進行覆蓋。
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex); // 計算新的集合的長度,方便后續(xù)進行多余數(shù)組元素的置空
for (int i = newSize; i < size; i++) {
elementData[i] = null; // 將后半部分的多余位置的元素賦值為 null,方便GC。
}
size = newSize; // 重新設(shè)置集合的大小
}源碼刨析:
- 這個方法可以指定刪除一段下標區(qū)間的所有元素。
- 首先進行修改次數(shù)的累加:
modCount++;,然后計算刪除區(qū)間的結(jié)束下標[toIndex]之后的元素數(shù)量,也就是numMoved,這部分元素需要往前移動來覆蓋待刪除區(qū)間的位置。 - 以數(shù)組拷貝的方式:
System.arraycopy將后半部分有效元素往前移動覆蓋掉刪除區(qū)間的位置。 - 重新計算集合的大?。?code>int newSize = size - (toIndex-fromIndex),方便后續(xù)將后半部分空缺的位置的數(shù)據(jù)進行無效化。
- 通過循環(huán)遍歷將后部分
無效的集合元素進行賦值null的操作,方便 GC 回收。 - 重新設(shè)置集合的大?。?code>size = newSize;
以上就是Java集合框架概覽之ArrayList源碼刨析的詳細內(nèi)容,更多關(guān)于Java ArrayList的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java.lang.IllegalStateException異常原因和解決辦法
這篇文章主要給大家介紹了關(guān)于java.lang.IllegalStateException異常原因和解決辦法,IllegalStateException是Java標準庫中的一個異常類,通常表示在不合適或無效的情況下執(zhí)行了某個方法或操作,需要的朋友可以參考下2023-07-07
Spring?@bean和@component注解區(qū)別
本文主要介紹了Spring?@bean和@component注解區(qū)別,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01
SpringCloud基于Feign實現(xiàn)遠程調(diào)用的問題小結(jié)
這篇文章主要介紹了SpringCloud基于Feign遠程調(diào)用,通過使用 Feign 的方式,我們可以更加優(yōu)雅地進行多參數(shù)的遠程調(diào)用,避免了手動拼接URL或構(gòu)建復(fù)雜的請求體,需要的朋友可以參考下2024-02-02
SpringMVC參數(shù)的傳遞之如何接收List數(shù)組類型的數(shù)據(jù)
這篇文章主要介紹了SpringMVC參數(shù)的傳遞之如何接收List數(shù)組類型的數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10

