點贊在看,養成習慣。
點贊收藏,人生輝煌。
點擊關注【微信搜索公衆號:編程背鍋俠】,防止迷路。
轉載請標註來源出處,謝謝合作。
前言
看源碼血淚史
剛開始工作面試的時候,面試官經常問ArrayList源碼相關的問題,基本上都是這部分很快結束戰鬥。
- 面試官:你看過ArrayList的源碼嗎?
- 我:你肯定會說看過呀。
- 面試官:那你來講講你對ArrayList源碼的理解吧。
- 我:底層的數據結構是object數組;增刪快、查詢慢等等,沒說幾句就完了。
其實看了ArrayList的源碼以後,你會發現能說的點還是有很多的。
比如ArrayList的構造方法的底層數組真的是構造了一個長度爲10的數組嗎?
Arrays.copy方法,grow擴容方法是怎麼擴容的?等等都可以細說。
ArrayList的源碼從工作到現在大概看了不下10遍,這其中包括看了半道放棄的。
剛開始看源碼是在一些博客網站上看,看的稀裏糊塗不是很明白,越看越想放棄。
後面看了一些公開課,跟着老師講的視頻看源碼,看完之後感覺有點意思。但是看完之後,自己單獨看還是有點喫力。
2020年4月份的時候看了一遍ArrayList源碼並且每行都做了註釋,整理在了有道上。
現在是七月初時隔兩個月在再次看源碼發現以前的筆記有部分是模糊、或者理解不正確的。
目前我發佈出來的ArrayList源碼是我一步一步DEBUG調試驗證的源碼。如果理解有問題看過之後,還請多多指教。
ArrayList系列文章
第一篇:ArrayList中的構造方法源碼在面試中被問到了…抱歉沒準備好!!!告辭
第二篇:面試官讓我講ArrayList中add、addAll方法的源碼…我下次再來
第三篇:工作兩年還沒看過ArrayList中remove、removeAll、clear方法源碼的都來報道吧
刪除方法表格
方法名 | 描述 |
---|---|
public E remove(int index) | 根據索引刪除元素 |
public boolean remove(Object o) | 根據元素刪除元素 |
public void clear() | 將集合清空 |
public boolean removeAll(Collection<?> c) | 刪除與給定集合中相同的元素 |
public E remove(int index) 根據索引刪除元素
案例演示
@Test
public void test_remove_index(){
ArrayList<String> list = new ArrayList<>();
list.add("洛洛01");
list.add("洛洛02");
list.add("洛洛03");
list.forEach(System.out::println);
// 索引刪除
list.remove(1);
list.forEach(System.out::println);
}
源碼分析
public E remove(int index) {
// 校驗這個索引是否在集合中存在
rangeCheck(index);
// 記錄修改的次數
modCount++;
// 將index對應的元素賦值給 oldValue
E oldValue = elementData(index);
// 計算集合中需要移動元素個數
int numMoved = size - index - 1;
// 判斷要移動的元素個數是否大於0
if (numMoved > 0)
// 能進到這裏面要刪除的元素肯定不在集合的最後面
// 如果需要移動元素個數大於0,就使用arrayCopy方法進行拷貝
// 注意:數據源和目標數據都是elementData
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 將源集合最後一個元素置爲null,儘早讓垃圾回收機制對其進行回收
elementData[--size] = null; // clear to let GC do its work
// 返回被刪除的元素
return oldValue;
}
elementData數組中元素的變化
-
源數組中的元素
-
System.arraycopy執行前數組中元素
- System.arraycopy執行後數組中元素-1
- System.arraycopy執行後數組中元素-2
總結
根據索引刪除元素,返回被刪除的元素。重點關注elementData數組中元素的變化,可以幫助理解。
public boolean remove(Object o) 根據元素刪除元素
案例演示
@Test
public void test_remove_v(){
ArrayList<String> list = new ArrayList<>();
list.add("洛洛01");
list.add("洛洛02");
list.add("洛洛03");
list.add("洛洛04");
list.forEach(System.out::println);
// 值刪除
list.remove("洛洛03");
list.forEach(System.out::println);
}
源碼分析
public boolean remove(Object o) {
// 判斷要刪除的元素是否爲null
if (o == null) {
// 被刪除的元素爲null,遍歷這個集合
for (int index = 0; index < size; index++)
// 判斷集合的元素是否爲null
if (elementData[index] == null) {
// 如果相等,調用fastRemove方法快速刪除
fastRemove(index);
return true;
}
} else {
// 被刪除的元素不爲空,遍歷集合
for (int index = 0; index < size; index++)
// 用o對象的equals方法和集合每一個元素進行比較
if (o.equals(elementData[index])) {
// 如果相等,調用fastRemove方法快速刪除
fastRemove(index);
return true;
}
}
// 如果集合沒有o該元素,那麼就會返回false
return false;
}
// 根據索引快速刪除方法
private void fastRemove(int index) {
// 記錄修改的次數
modCount++;
// 計算要移動元素的個數
int numMoved = size - index - 1;
// 如果需要移動的個數大於0,調用arrayCopy方法進行拷貝,判斷是不是在尾部插入,大於0不是在尾部
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 將集合最後一個元素置爲null,儘早被釋放
elementData[--size] = null; // clear to let GC do its work
}
elementData數組中元素的變化
-
源數組中的元素
-
System.arraycopy執行前數組中元素
-
System.arraycopy執行後數組中元素
總結
根據給定的元素刪除集合中與之匹配的元素。返回值爲是否刪除成功的布爾值。
public void clear()將集合清空
案例演示
@Test
public void test_clear(){
ArrayList<String> list = new ArrayList<>();
list.add("洛洛01");
list.add("洛洛02");
list.forEach(System.out::println);
list.clear();
list.forEach(System.out::println);
}
源碼分析
public void clear() {
// 實際修改集合次數++
modCount++;
// 遍歷集合,將集合每一個索引對應位置上的元素都置爲null,儘早讓其釋放
for (int i = 0; i < size; i++)
elementData[i] = null;
// 集合長度更改爲0
size = 0;
}
elementData數組中元素的變化
-
源數組中的元素
-
清空以後的數組
總結
將集合清空。這個方法會將集合每一個索引對應位置上的元素都置爲null,爲的是儘早讓垃圾收集器回收。
public boolean removeAll(Collection<?> c)刪除與給定集合中相同的元素
案例演示
@Test
public void test_remove_all(){
ArrayList<String> list = new ArrayList<>();
list.add("洛洛01");
list.add("洛洛02");
list.forEach(System.out::println);
ArrayList<String> all = new ArrayList<>();
all.add("洛洛01");
all.add("洛洛05");
list.removeAll(all);
list.forEach(System.out::println);
}
源碼分析
public boolean removeAll(Collection<?> c) {
// 校驗集合是否爲空,爲空拋出空指針異常
Objects.requireNonNull(c);
// 批量刪除
return batchRemove(c, false);
}
elementData數組中元素的變化
-
源數組中的元素
-
源數組變化以後的元素
總結
刪除與給定集合中相同的元素。這個方法的主要實現是
batchRemove
方法。
private boolean batchRemove(Collection<?> c, boolean complement)批量刪除方法
private boolean batchRemove(Collection<?> c, boolean complement) {
// 將原始數組的地址賦值給elementData
final Object[] elementData = this.elementData;
// r:用於遍歷原始數組,原始數組中元素的索引, w:記錄的是未被刪除元素的個數
int r = 0, w = 0;
// modified:是否刪除成功給個默認值false
boolean modified = false;
try {
// 遍歷原始數組,size爲原始數組的長度
for (; r < size; r++)
// complement的給定的值爲false,判斷指定的集合c是否不包含這個元素
if (c.contains(elementData[r]) == complement)
// 指定的集合c中不包含原始數組中的元素,將這個元素放到elementData數組中。
// 這個循環執行完畢,elementData數組中存放的就是從索引0開始存放未被刪除的元素,和後面可能有要被刪除的和未被刪除的元素,總的長度是原始數組的size。被刪除的元素會留在原位置,未被刪除的元素原位置有一份,還有一份複製到前面。
elementData[w++] = elementData[r];
} finally {
// 正常情況下r == size的,這個不等於是拋出了異常
if (r != size) {
// 數組的拷貝,參看我的其他文章,有這個方法的源碼詳解
System.arraycopy(elementData, r,
elementData, w,
size - r);
// 計算修改的次數
w += size - r;
}
// 判斷w【記錄未被刪除的元素的個數】是否等於元素數組的長度
if (w != size) {
// clear to let GC do its work,方便垃圾回收,將elementData數組從索引i=w開始,置空每個元素
for (int i = w; i < size; i++)
// 置空元素
elementData[i] = null;
// size - w刪除元素的個數,modCount記錄的是修改的次數,每刪除一個元素modCount加1
modCount += size - w;
// 刪除執行完以後集合的長度
size = w;
// 刪除成功modified 賦值爲true
modified = true;
}
}
// 返回是否刪除成功
return modified;
}
創作不易, 非常歡迎大家的點贊、評論和關注(^_−)☆
你的點贊、評論以及關注是對我最大的支持和鼓勵,而你的支持和鼓勵
我繼續創作高質量博客的動力 !!!