ArrayList 內容很多,可以先從總體架構入手,然後再以某個細節作為突破口,比如這樣:ArrayList 底層資料結構是個陣列,其 API 都做了一層對陣列底層存取的封裝,比如說 add 方法的過程是……(這裡具體可以參考 ArrayList 原始碼分析中 add 的過程)。
另外,對 LinkedList 的理解也是同樣套路。
答:此處陣列的大小是 1,下一次擴容前最大可用大小是 10,因為 ArrayList 第一次擴容時,是有預設值的,預設值是 10,在第一次 add 一個值進去時,陣列的可用大小被擴容到 10 了。
答:這裡實際問的是擴容的公式,當增加到 11 的時候,此時我們希望陣列的大小為 11,但實際上陣列的最大容量只有 10,不夠了就需要擴容,擴容的公式是:oldCapacity + (oldCapacity>> 1),oldCapacity 表示陣列現有大小,目前場景計算公式是:10 + 10 /2 = 15,然後我們發現 15 已經夠用了,所以陣列的大小會被擴容到 15。
答:在上一題中已經計算出來陣列在加入一個值後,實際大小是 1,最大可用大小是 10 ,現在需要一下子加入 15 個值,那我們期望陣列的大小值就是 16,此時陣列最大可用大小隻有 10,明顯不夠,需要擴容,擴容後的大小是:10 + 10 /2 = 15,這時候發現擴容後的大小仍然不到我們期望的值 16,這時候原始碼中有一種策略如下:
// newCapacity 本次擴容的大小,minCapacity 我們期望的陣列最小大小
// 如果擴容後的值 < 我們的期望值,我們的期望值就等於本次擴容的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
所以最終陣列擴容後的大小為 16。具體原始碼請參考ArrayList 原始碼分析的grow方法。
答:因為原陣列比較大,如果新建新陣列的時候,不指定陣列大小的話,就會頻繁擴容,頻繁擴容就會有大量拷貝的工作,造成拷貝的效能低下,所以在新建陣列時,指定新陣列的大小為 5k 即可。
答:擴容底層使用的是 System.arraycopy 方法,會把原陣列的資料全部拷貝到新陣列上,所以效能消耗比較嚴重。
答:有兩點:
List<String> list = new ArrayList<String>() {{
add("2");
add("3");
add("3");
add("3");
add("4");
}};
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("3")) {
list.remove(i);
}
}
答:不能刪除乾淨,最終刪除的結果是 2、3、4,有一個 3 刪除不掉,原因我們看下圖:
從圖中我們可以看到,每次刪除一個元素後,該元素後面的元素就會往前移動,而此時迴圈的 i 在不斷地增長,最終會使每次刪除 3 的後一個 3 被遺漏,導致刪除不掉。
答:不可以,會報錯。因為增強 for 迴圈過程其實呼叫的就是迭代器的 next () 方法,當你呼叫 list.remove () 方法進行刪除時,modCount 的值會 +1,而這時候迭代器中的 expectedModCount 的值卻沒有變,導致在迭代器下次執行 next () 方法時,expectedModCount != modCount 就會報 ConcurrentModificationException 的錯誤。
答:可以的,因為 Iterator.remove () 方法在執行的過程中,會把最新的 modCount 賦值給 expectedModCount,這樣在下次迴圈過程中,modCount 和 expectedModCount 兩者就會相等。
答:是的,雖然 LinkedList 底層結構是雙向連結串列,但對於上述三個問題,結果和 ArrayList 是一致的。
答:可以先從底層資料結構開始說起,然後以某一個方法為突破口深入,比如:
答:
答:
答:
答:
如果有執行緒安全問題,在迭代的過程中,會頻繁報 ConcurrentModificationException 的錯誤,意思是在我當前迴圈的過程中,陣列或連結串列的結構被其它執行緒修改了
答:Java 原始碼中推薦使用 Collections#synchronizedList 進行解決,Collections#synchronizedList 的返回值是 List 的每個方法都加了 synchronized 鎖,保證了在同一時刻,陣列和連結串列只會被一個執行緒所修改,但是效能大大降低,具體實現原始碼:
public boolean add(E e) {
synchronized (mutex) {// synchronized 是一種輕量鎖,mutex 表示一個當前 SynchronizedList
return c.add(e);
}
}
另外,還可以採用 JUC 的 CopyOnWriteArrayList 並行 List 來解決。