Java中的ArrayList(擴(kuò)容機(jī)制)詳解
背景介紹
對(duì)于ArrayList集合可能大家并不陌生,但ArrayList集合的擴(kuò)容機(jī)制大家是否了解呢?我們今天著重來(lái)看看
適用于什么場(chǎng)景?
檢索比較多的場(chǎng)景
ArrayList特點(diǎn)
- 1、ArrayList集合底層采用了數(shù)據(jù)這種數(shù)據(jù)結(jié)構(gòu),是Object類型
- 2、ArrayList的默認(rèn)初始容量為10,擴(kuò)容因子為1.5
- 3、建議給定一個(gè)預(yù)估計(jì)的初始化容量,減少數(shù)組擴(kuò)容的次數(shù),這是ArrayList集合比較重要的優(yōu)化策略.因?yàn)樵谠跀U(kuò)容的同時(shí)需要將原來(lái)數(shù)組中的數(shù)據(jù)復(fù)制到新數(shù)組里,但如果要插入大量數(shù)據(jù)時(shí),賦值數(shù)組的形式效率很低,所以大多數(shù)情況下會(huì)使用帶參構(gòu)造函數(shù),傳入一個(gè)預(yù)估計(jì)容量,提前定義好容量。
- 4、ArrayList是非線程安全的
實(shí)戰(zhàn)演練
import java.util.ArrayList; import java.util.List; public class ListTest { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("b");//第一個(gè),索引下標(biāo)0 list.add("d"); list.add("c"); list.add("a"); list.add("d"); //允許使用重復(fù)元素 System.out.println(list); //輸出結(jié)果:[b, d, c, a, d] System.out.println(list.get(2)); //輸出指定下標(biāo)的元素,輸出結(jié)果:c list.add(1,"f");//在指定索引下標(biāo)位置添加元素 System.out.println(list); //輸出結(jié)果:[b, f, d, c, a, d],原來(lái)下標(biāo)為1和1之后的下標(biāo)索引位置的元素自動(dòng)向后移動(dòng) List<String> a = new ArrayList<String>(); a.add("123"); a.add("456"); list.addAll(2,a); //在指定索引下標(biāo)的位置插入集合 System.out.println(list);//輸出結(jié)果:[b, f, 123, 456, d, c, a, d] //獲取指定元素在集合中第一次出現(xiàn)的索引下標(biāo) System.out.println(list.indexOf("d")); //輸出結(jié)果:4 //獲取指定元素在集合中最后一次出現(xiàn)的索引下標(biāo) System.out.println(list.lastIndexOf("d"));//輸出結(jié)果:7 list.remove(2); //根據(jù)指定的索引下標(biāo)移除元素 System.out.println(list); //輸出結(jié)果:[b, f, 456, d, c, a, d] list.set(1,"ff"); //根據(jù)指定的索引下標(biāo)修改元素 System.out.println(list); //輸出結(jié)果:[b, ff, 456, d, c, a, d] //根據(jù)索引下標(biāo)的起始位置截取一段元素形成一個(gè)新的集合,截取的時(shí)候,包含開(kāi)始的索引不包含結(jié)束時(shí)的索引 List<String> sublist= list.subList(2,4); System.out.println(sublist);//輸出結(jié)果:[456, d] System.out.println(list.size());//輸出結(jié)果7 } }
import java.util.LinkedList; import java.util.List; public class ListTest { public static void main(String[] args){ List l1 = new LinkedList(); for(int i = 0;i<=5;i++){ l1.add("a"+i); } System.out.print(l1); l1.add(3,"a100"); System.out.println(l1); l1.set(6,"a200"); System.out.println(l1); System.out.print((String)l1.get(2)+" "); System.out.println(l1.indexOf("a3")); l1.remove(1); System.out.println(l1); } }
輸出結(jié)果:
[a0,a1,a2,a3,a4,a5]
[a0,a1,a2,a100,a3,a4,a5]
[a0,a1,a2,a100,a3,a4,a200]
a2 4
[a0,a2,a100,a3,a4,a200]
ArrayList擴(kuò)容機(jī)制
ArrayList的使用前不需要像數(shù)組一樣提前定義大小空間,容量是隨著使用時(shí)自動(dòng)增長(zhǎng)的,那為什么在使用ArrayList的add方法添加元素的時(shí)候底層還需要判斷集合的容量是否能夠放下要添加的元素呢?
又沒(méi)有定義固定大小直接放進(jìn)去不就好了嗎?
add方法添加分為三步:
- ①、判斷集合容量是否滿足添加的元素
- ②、添加元素
- ③、集合長(zhǎng)度+1
解答:
用戶不需要提前定義大小,那是因?yàn)榈讓幽J(rèn)已經(jīng)定義好了大小。
其實(shí)是有一個(gè)邊界值的,并不是無(wú)限增長(zhǎng)的。
使用時(shí)增加,是因?yàn)榈讓佑袛U(kuò)展因子(擴(kuò)容因子是1.5),當(dāng)數(shù)量達(dá)到數(shù)組的百分之多少的時(shí)候就會(huì)擴(kuò)容。ArrayList默認(rèn)的初始大小是10
問(wèn)題:大家可以思考思考為什么ArrayList底層擴(kuò)容因子是1.5?為什么不是1.3、2.4……?
ArrayList的底層擴(kuò)容因子是1.5,而不是其他數(shù)字,是為了在平衡內(nèi)存使用和性能之間找到一個(gè)合適的折中方案。
下面是一些原因:
- 內(nèi)存分配的效率:擴(kuò)容因子的選擇會(huì)影響內(nèi)存分配的效率。如果擴(kuò)容因子過(guò)小,每次擴(kuò)容都只增加少量的容量,這會(huì)導(dǎo)致頻繁的內(nèi)存分配操作,增加了時(shí)間和空間的開(kāi)銷。而如果擴(kuò)容因子過(guò)大,每次擴(kuò)容都會(huì)增加大量的容量,這可能會(huì)導(dǎo)致浪費(fèi)過(guò)多的內(nèi)存。1.5是一個(gè)相對(duì)較小的擴(kuò)容因子,可以在一定程度上平衡內(nèi)存使用和性能。
- 數(shù)據(jù)遷移的代價(jià):當(dāng)ArrayList需要擴(kuò)容時(shí),需要將原有數(shù)據(jù)遷移到新的更大的數(shù)組中。擴(kuò)容因子的選擇會(huì)影響數(shù)據(jù)遷移的頻率和代價(jià)。較小的擴(kuò)容因子會(huì)導(dǎo)致更頻繁的數(shù)據(jù)遷移,而較大的擴(kuò)容因子會(huì)減少數(shù)據(jù)遷移的次數(shù)。1.5作為一個(gè)相對(duì)較小的擴(kuò)容因子,可以在一定程度上減少數(shù)據(jù)遷移的代價(jià)。
- 性能和空間的平衡:ArrayList旨在提供高效的隨機(jī)訪問(wèn)和動(dòng)態(tài)增長(zhǎng)的能力。選擇1.5作為擴(kuò)容因子可以在一定程度上平衡性能和空間的需求。較小的擴(kuò)容因子可以減少內(nèi)存的浪費(fèi),而較大的擴(kuò)容因子可以減少內(nèi)存分配的頻率。
綜上所述,ArrayList的特點(diǎn)如下
- 1、ArrayList集合底層采用了數(shù)據(jù)這種數(shù)據(jù)結(jié)構(gòu),是Object類型
- 2、ArrayList的默認(rèn)初始容量為10,擴(kuò)容因子為1.5
- 3、建議給定一個(gè)預(yù)估計(jì)的初始化容量,減少數(shù)組擴(kuò)容的次數(shù),這是ArrayList集合比較重要的優(yōu)化策略.因?yàn)樵谠跀U(kuò)容的同時(shí)需要將原來(lái)數(shù)組中的數(shù)據(jù)復(fù)制到新數(shù)組里,但如果要插入大量數(shù)據(jù)時(shí),賦值數(shù)組的形式效率很低,所以大多數(shù)情況下會(huì)使用帶參構(gòu)造函數(shù),傳入一個(gè)預(yù)估計(jì)容量,提前定義好容量。
- 4、ArrayList是非線程安全的
總結(jié)
需要注意的是,擴(kuò)容因子的選擇并不是一個(gè)固定的標(biāo)準(zhǔn),可以根據(jù)具體的應(yīng)用場(chǎng)景和性能需求進(jìn)行調(diào)整。
在實(shí)際使用中,可以根據(jù)經(jīng)驗(yàn)和性能測(cè)試來(lái)選擇合適的擴(kuò)容因子,以滿足應(yīng)用的需求。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java創(chuàng)建子類對(duì)象設(shè)置并調(diào)用父類的變量操作
這篇文章主要介紹了java創(chuàng)建子類對(duì)象設(shè)置并調(diào)用父類的變量操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01Spring MVC的優(yōu)點(diǎn)與核心接口_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Spring MVC的優(yōu)點(diǎn)與核心接口,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08解讀Integer類的parseInt和valueOf的區(qū)別
這篇文章主要介紹了解讀Integer類的parseInt和valueOf的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11Intellij IDEA 配置Subversion插件實(shí)現(xiàn)步驟詳解
這篇文章主要介紹了Intellij IDEA 配置Subversion插件實(shí)現(xiàn)步驟詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05mybatis in查詢條件過(guò)長(zhǎng)的解決方案
這篇文章主要介紹了mybatis in查詢條件過(guò)長(zhǎng)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10詳解Java 序列化與反序列化(Serialization)
這篇文章主要介紹了Java 序列化與反序列化(Serialization),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí) 吧2019-03-03