Java實(shí)現(xiàn)ArrayList自動(dòng)擴(kuò)容
一.為什么要進(jìn)行擴(kuò)容
1.1ArrayLiat底層介紹
ArrayList`是 Java 中的一個(gè)常用集合類,其內(nèi)部實(shí)現(xiàn)是基于數(shù)組的,可以通過下標(biāo)來訪問和修改其中的元素。在操作 `ArrayList` 時(shí),如果我們向其中添加的元素個(gè)數(shù)超過了已分配的數(shù)組長(zhǎng)度,則需要對(duì)數(shù)組進(jìn)行擴(kuò)容。這也是 `ArrayList` 能夠自動(dòng)擴(kuò)容的原因。
1.2擴(kuò)容原因:
下面是對(duì) ArrayList 擴(kuò)容的必要性進(jìn)行解釋:
1. 提高效率
數(shù)組在內(nèi)存中是一段連續(xù)的空間,在同一時(shí)間內(nèi),其所有元素的空間是連續(xù)的。在 `ArrayList` 中,當(dāng)我們需要向其中添加元素時(shí),如果發(fā)現(xiàn)當(dāng)前數(shù)組的長(zhǎng)度已經(jīng)達(dá)到了其最大值,我們就需要對(duì)其進(jìn)行擴(kuò)容。擴(kuò)容的意思是在內(nèi)存中重新分配一個(gè)可以容納更多元素的連續(xù)空間,并將原有數(shù)組的元素復(fù)制到新的空間中。這樣,我們就可以向新的空間中添加元素了。通過擴(kuò)容,我們避免了在數(shù)組中頻繁移動(dòng)元素,提高了程序的效率。
2. 避免數(shù)組下標(biāo)越界
當(dāng)我們?cè)L問數(shù)組時(shí),如果使用的下標(biāo)超出了數(shù)組的實(shí)際長(zhǎng)度,則會(huì)發(fā)生數(shù)組下標(biāo)越界異常。擴(kuò)容可以避免這種情況發(fā)生,因?yàn)閿U(kuò)容會(huì)重新分配更大的內(nèi)存空間,并修改數(shù)組的長(zhǎng)度,使得我們可以在新的空間中添加元素,避免了訪問數(shù)組時(shí)下標(biāo)越界的問題。
3. 降低內(nèi)存碎片
擴(kuò)容可以避免內(nèi)存碎片的產(chǎn)生。在執(zhí)行增刪操作時(shí),如果數(shù)組中存在空洞,即有些位置的元素被刪除后,造成數(shù)組遠(yuǎn)小于原來的大小,這時(shí)也需要重新分配內(nèi)存空間。如果這種情況發(fā)生頻繁,會(huì)導(dǎo)致內(nèi)存空間的碎片化,使得程序性能變差。而數(shù)組的擴(kuò)容操作在內(nèi)存中分配連續(xù)空間,可以避免這種問題的出現(xiàn),從而降低內(nèi)存碎片的產(chǎn)生。
1.3什么是內(nèi)存碎片化?
1.3.1定義:
指在程序運(yùn)行過程中,由于空間的分配和釋放不規(guī)則,形成了很多不連續(xù)、空閑的內(nèi)存片段,使得原本容易找到連續(xù)空間的程序運(yùn)行變得困難。內(nèi)存中的空閑區(qū)域是不連續(xù)的,當(dāng)需要分配一個(gè)大段的內(nèi)存時(shí),就會(huì)因?yàn)檎也坏阶銐虻倪B續(xù)空間而產(chǎn)生問題。這種情況常常發(fā)生在反復(fù)分配和釋放內(nèi)存的場(chǎng)景中,例如大數(shù)據(jù)量的操作或多次執(zhí)行動(dòng)態(tài)數(shù)組擴(kuò)容等。
1.3.2解決方法
內(nèi)存空間的碎片化會(huì)對(duì)系統(tǒng)性能產(chǎn)生負(fù)面影響,因為程序在分配或釋放內(nèi)存時(shí)需要不停地將內(nèi)存碎片合并或分割,這樣會(huì)浪費(fèi)更多的時(shí)間和系統(tǒng)資源。在程序的現(xiàn)代化中,應(yīng)該盡可能地避免或減少內(nèi)存碎片的出現(xiàn),從而優(yōu)化程序性能。
以下是幾種減少內(nèi)存碎片的方法:
- 使用內(nèi)存池技術(shù),固定分配一部分內(nèi)存塊供程序使用。
- 動(dòng)態(tài)分配時(shí),采用內(nèi)存對(duì)齊方式,盡量使得分配出來的內(nèi)存塊尺寸是確定的。
- 盡量減少對(duì)象的分配與回收,從而降低內(nèi)存碎片化的程度。
- 對(duì)于數(shù)據(jù)結(jié)構(gòu)動(dòng)態(tài)增長(zhǎng)的場(chǎng)景,采用增量式擴(kuò)容的策略,避免一次性分配過大的內(nèi)存空間。
二.進(jìn)行ArrayLiat擴(kuò)容操作
1.擴(kuò)容的底層原理:
ArrayList 自動(dòng)擴(kuò)容的實(shí)現(xiàn):依賴于 `ensureCapacityInternal()` 和 `grow()` 兩個(gè)方法。
當(dāng)添加一個(gè)新元素到 ArrayList 時(shí),ArrayList 會(huì)先判斷當(dāng)前存儲(chǔ)元素的數(shù)組容量是否已經(jīng)達(dá)到了最大大?。?`Integer.MAX_VALUE`),如果已經(jīng)達(dá)到了則不再擴(kuò)容。否則,如果當(dāng)前存儲(chǔ)元素的數(shù)組容量已經(jīng)滿了,那么就需要擴(kuò)容:
1. `ensureCapacityInternal()` 方法會(huì)先計(jì)算出新的容量大小:原來的容量大?。ㄓ洖?oldCapacity)加上原來的容量大小的一半(即 oldCapacity >> 1)。
2. 如果新的容量大小小于當(dāng)前所需的容量(即當(dāng)前 arrayList 的大小加 1),則新容量大小為當(dāng)前所需的容量加 1。
3. `grow()` 方法會(huì)創(chuàng)建一個(gè)新的存儲(chǔ)元素的數(shù)組,其大小就是新的容量大小。然后它會(huì)調(diào)用 `Arrays.copyOf()` 方法來將舊數(shù)組中的元素復(fù)制到新數(shù)組中。
最后,ArrayList 會(huì)將新的數(shù)組作為內(nèi)部存儲(chǔ)數(shù)組,并更新相關(guān)信息。
這樣,每次擴(kuò)容的大小為原數(shù)組大小的約1.5倍,這個(gè)值是可以設(shè)置的,通過 `ensureCapacity()` 方法來調(diào)整。如果需要添加很多元素,建議預(yù)估需要的大小,調(diào)用 `ensureCapacity()` 來避免頻繁的擴(kuò)容操作,提高性能。
2.實(shí)現(xiàn)操作
2.1代碼截屏:
2.2效果圖:
2.3注意事項(xiàng):
1.ArrayList 默認(rèn)長(zhǎng)度為10,當(dāng)然我們也可以設(shè)置長(zhǎng)度為其他值
例如: ArrayList list=new ArrayList<>(50); 當(dāng)我們打印集合長(zhǎng)度是就為:50
好處:可以減少擴(kuò)容次數(shù),提高程序在處理,運(yùn)行時(shí)的速度
2. ArrayList 的增長(zhǎng)因子為0.5倍數(shù),擴(kuò)容倍數(shù)為1.5倍數(shù)!在進(jìn)行擴(kuò)容是數(shù)組長(zhǎng)度在有小數(shù)時(shí)會(huì)向下取整比如說:在效果圖數(shù)組長(zhǎng)度15時(shí):在添加第16個(gè)元素時(shí)擴(kuò)容后的長(zhǎng)度為:原來長(zhǎng)度*0.5+原來長(zhǎng)度(向下取整)=新長(zhǎng)度;運(yùn)用這個(gè)公式就可以得到新的擴(kuò)容數(shù)組長(zhǎng)度為22
2.4源碼:
package liuzhi_list; import java.lang.reflect.Field; import java.util.ArrayList; public class demo1 { public static void main(String[] args) throws Exception{ //首先創(chuàng)建一個(gè)arrayList 集合 ArrayList list=new ArrayList<>(50); //通過for循環(huán)添加數(shù)據(jù) 添加100條 for (int i = 0; i<100; i++) { list.add(i); //打印輸出結(jié)果 System.out.print(i+"\r"); //發(fā)現(xiàn)集合通過for循環(huán)增加了100條數(shù)據(jù) System.out.println("集合有多少數(shù)據(jù):"+list.size()); getLength(list); } } /** * 此方法通過調(diào)用arrayliat底層來操作 * @param list * @throws Exception * @throws Exception */ private static void getLength(ArrayList list) throws Exception, Exception { //通過調(diào)用 獲取 ArrayList對(duì)象類部類獲取底層的elementData字段 Field f = list.getClass().getDeclaredField("elementData"); //因?yàn)閍rraylist底層是私有化 必須通過反射 并且打開私有化權(quán)限 f.setAccessible(true); //作用是獲取 ArrayList 對(duì)象的底層數(shù)組 Object[] object = (Object[]) f.get(list); //打印數(shù)組長(zhǎng)度 System.out.println("當(dāng)前數(shù)組長(zhǎng)度: "+object.length); } }
到此這篇關(guān)于Java實(shí)現(xiàn)ArrayList自動(dòng)擴(kuò)容的文章就介紹到這了,更多相關(guān)Java ArrayList自動(dòng)擴(kuò)容內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用JAXBContext輕松實(shí)現(xiàn)Java和xml的互相轉(zhuǎn)換方式
這篇文章主要介紹了依靠JAXBContext輕松實(shí)現(xiàn)Java和xml的互相轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Java基礎(chǔ)知識(shí)之CharArrayWriter流的使用
這篇文章主要介紹了Java基礎(chǔ)知識(shí)之CharArrayWriter流的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12spring boot中使用@Async實(shí)現(xiàn)異步調(diào)用任務(wù)
本篇文章主要介紹了spring boot中使用@Async實(shí)現(xiàn)異步調(diào)用任務(wù),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02Java使用默認(rèn)瀏覽器打開指定URL的方法(二種方法)
Java使用默認(rèn)瀏覽器打開指定URL。2013-10-10SpringBoot項(xiàng)目從搭建到發(fā)布一條龍
這篇文章主要介紹了SpringBoot項(xiàng)目從搭建到發(fā)布一條龍,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02全網(wǎng)最全最細(xì)的jmeter接口測(cè)試教程以及接口測(cè)試流程(入門教程)
本文主要介紹了全網(wǎng)最全最細(xì)的jmeter接口測(cè)試教程以及接口測(cè)試流程,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11