服務創造價值、存在(zài)造就(jiù)未來(lái)
今天我們來(lái)聊一(yī / yì /yí)聊 Java 中經常使用的(de)兩個(gè)集合類:ArrayList和(hé / huò)LinkedList。作爲(wéi / wèi) Java 開發的(de)經典基礎,ArrayList 和(hé / huò) LinkedList 常常會因爲(wéi / wèi)它們的(de)底層實現和(hé / huò)操作方式的(de)不(bù)同而(ér)被拿來(lái)對比,大(dà)家在(zài)開發中也(yě)會針對不(bù)同的(de)使用場景選擇最适合的(de)集合類型。那接下來(lái),咱們就(jiù)一(yī / yì /yí)起看看這(zhè)兩個(gè)家夥的(de)各自特點吧~
ArrayList的(de)底層實現:ArrayList,顧名思義,它的(de)底層實現其實是(shì)一(yī / yì /yí)個(gè)動态數組。這(zhè)個(gè)“動态”體現在(zài)我們可以(yǐ)通過 ArrayList 随時(shí)添加、删除元素,而(ér)不(bù)會像數組那樣必須初始化一(yī / yì /yí)個(gè)固定大(dà)小才能用。它的(de)底層數組會随着數據的(de)增長不(bù)斷擴容,讓我們有種“空間無限”的(de)感覺~不(bù)過,擴容其實是(shì)有代價的(de)。
ArrayList的(de)擴容機制:ArrayList 的(de)默認初始容量是(shì)10。當元素數量超過當前數組容量時(shí),就(jiù)會觸發擴容機制。默認情況下,ArrayList 的(de)容量會增加到(dào)原來(lái)的(de) 1.5 倍,然後把舊數組的(de)内容複制到(dào)新的(de)更大(dà)數組中。這(zhè)種擴容方式雖然保證了(le/liǎo) ArrayList 有更大(dà)的(de)存儲空間,但擴容時(shí)的(de)數據複制會帶來(lái)一(yī / yì /yí)定的(de)性能損耗。所以(yǐ),建議大(dà)家在(zài)創建 ArrayList 時(shí),如果已經大(dà)緻知道(dào)需要(yào / yāo)的(de)容量,可以(yǐ)通過 new ArrayList<>(capacity) 來(lái)提前指定容量,減少擴容次數,提升性能。
随機訪問的(de)優勢:ArrayList 是(shì)基于(yú)數組的(de),所以(yǐ)我們可以(yǐ)通過索引直接訪問任意元素,這(zhè)樣的(de)随機訪問速度非常快。時(shí)間複雜度是(shì) O(1),對開發者來(lái)說(shuō)無疑是(shì)福音!适合那些頻繁訪問特定位置數據的(de)場景,比如實現排行榜、購物車列表等等。
插入和(hé / huò)删除的(de)劣勢:然而(ér),當我們從中間位置插入或删除元素時(shí),由于(yú)數組的(de)結構特點,必須要(yào / yāo)移動後續的(de)所有元素才能保持數據的(de)順序,這(zhè)樣操作的(de)時(shí)間複雜度是(shì) O(n)。所以(yǐ) ArrayList 更适合于(yú)查詢多、增删少的(de)場景。
LinkedList的(de)底層實現:LinkedList 的(de)底層是(shì)一(yī / yì /yí)個(gè)雙向鏈表,這(zhè)就(jiù)意味着它的(de)每個(gè)節點都包含數據和(hé / huò)兩個(gè)指針,一(yī / yì /yí)個(gè)指向前一(yī / yì /yí)個(gè)節點,一(yī / yì /yí)個(gè)指向後一(yī / yì /yí)個(gè)節點。相較于(yú)數組,鏈表的(de)優勢在(zài)于(yú),鏈表不(bù)需要(yào / yāo)像數組那樣在(zài)内存中是(shì)連續的(de)。所以(yǐ) LinkedList 适用于(yú)頻繁插入和(hé / huò)删除的(de)場景。
靈活的(de)增删操作:鏈表的(de)優點就(jiù)是(shì)可以(yǐ)在(zài)任意位置進行增删操作,而(ér)不(bù)需要(yào / yāo)像 ArrayList 那樣進行大(dà)量的(de)數據移動。在(zài) LinkedList 中,我們可以(yǐ)輕松地(dì / de)将新節點插入到(dào)鏈表的(de)任意位置。這(zhè)讓 LinkedList 具備了(le/liǎo)比 ArrayList 更快的(de)插入和(hé / huò)删除性能,尤其是(shì)當操作數據量非常大(dà)的(de)時(shí)候,優勢更加明顯。
額外的(de)堆棧和(hé / huò)隊列操作:LinkedList 除了(le/liǎo)實現 List 接口外,還實現了(le/liǎo) Deque(雙端隊列)接口。因此,它還提供了(le/liǎo)許多在(zài) List 中沒有定義的(de)方法,比如 addFirst() 和(hé / huò) addLast(),這(zhè)些方法可以(yǐ)讓 LinkedList 輕松地(dì / de)當作堆棧、隊列、雙端隊列來(lái)使用。實際上(shàng),JDK 官方更推薦用基于(yú) LinkedList 的(de) Deque 來(lái)進行堆棧操作,比方說(shuō)當我們想使用一(yī / yì /yí)個(gè)棧數據結構時(shí),LinkedList 是(shì)個(gè)更優的(de)選擇。
這(zhè)兩者有很多共性,像是(shì)它們都不(bù)保證線程安全,都實現了(le/liǎo) List 接口。但在(zài)具體應用場景上(shàng),它們還是(shì)有很大(dà)區别的(de),大(dà)家可以(yǐ)參考下表:
總的(de)來(lái)說(shuō),ArrayList 适合查詢操作比較多的(de)場景,而(ér) LinkedList 則适合增加和(hé / huò)删除操作較頻繁的(de)場景。
雖然 ArrayList 和(hé / huò) LinkedList 默認是(shì)非線程安全的(de),但我們可以(yǐ)通過以(yǐ)下方式來(lái)實現它們的(de)線程安全。
使用 Vector:Vector 是(shì) ArrayList 的(de)早期實現,它通過 synchronized 關鍵字來(lái)保證線程安全。但是(shì)因爲(wéi / wèi)加鎖的(de)代價較高,所以(yǐ)性能會比較低。Vector 适用于(yú)簡單線程同步需求的(de)場景,但在(zài)高并發環境下不(bù)推薦使用。
Collections.synchronizedList:Java 提供了(le/liǎo) Collections.synchronizedList(List list) 方法,可以(yǐ)把 ArrayList 轉換成線程安全的(de)集合。這(zhè)種方式也(yě)是(shì)通過 synchronized 來(lái)實現同步的(de),因此并發性能也(yě)不(bù)高。
CopyOnWriteArrayList:更好的(de)方式是(shì)使用CopyOnWriteArrayList。這(zhè)是(shì) Java 并發包中的(de)一(yī / yì /yí)個(gè)集合類,底層實現了(le/liǎo)寫時(shí)複制的(de)機制。寫操作時(shí),它會先複制一(yī / yì /yí)份新的(de)數組進行修改,完成後再把引用指向這(zhè)個(gè)新數組。這(zhè)樣,讀操作就(jiù)不(bù)需要(yào / yāo)加鎖,性能非常高,非常适合讀多寫少的(de)場景。
小結:在(zài)多線程環境下,如果是(shì)寫操作不(bù)頻繁的(de)情況,建議使用 CopyOnWriteArrayList 來(lái)替代 ArrayList 或 LinkedList,可以(yǐ)避免因爲(wéi / wèi)鎖帶來(lái)的(de)性能損耗。
ArrayList 和(hé / huò) LinkedList 各有千秋:
ArrayList:基于(yú)數組實現,适合頻繁的(de)查詢操作,擴容會有數據複制開銷。推薦在(zài)數據量不(bù)頻繁變動的(de)情況下使用。
LinkedList:基于(yú)雙向鏈表實現,插入和(hé / huò)删除更爲(wéi / wèi)靈活,同時(shí)支持堆棧和(hé / huò)隊列操作,适合頻繁增删的(de)場景。
如果遇到(dào)多線程訪問需求,建議優先考慮CopyOnWriteArrayList這(zhè)樣的(de)并發集合類,避免不(bù)必要(yào / yāo)的(de)加鎖操作~
希望這(zhè)篇文章可以(yǐ)幫你們更好地(dì / de)理解 ArrayList 和(hé / huò) LinkedList 的(de)不(bù)同,用最合适的(de)數據結構來(lái)解決實際問題!加油,一(yī / yì /yí)起寫出(chū)更高效的(de)代碼吧~