詳解Java數(shù)組擴(kuò)容縮容與拷貝的實(shí)現(xiàn)和原理
一. 數(shù)組內(nèi)存分析(重點(diǎn))
1. 簡(jiǎn)介
Java的內(nèi)存,可以分為棧、堆、方法區(qū)、本地方法區(qū)、程序寄存器等幾個(gè)核心部分。這一塊的內(nèi)容,以后會(huì)專門編寫(xiě)文章進(jìn)行介紹,對(duì)于初學(xué)者來(lái)說(shuō),這還不適合我們學(xué)習(xí)。但是我們現(xiàn)在要先對(duì)以下三個(gè)概念有所了解:
棧: 棧中可以存儲(chǔ)基本類型的數(shù)據(jù)和引用類型的地址。特點(diǎn): 先進(jìn)后出,一般空間比較小,存取速度較快。
堆: 堆中可以存儲(chǔ)引用類型的數(shù)據(jù)。特點(diǎn): 空間比較大,存儲(chǔ)速度相對(duì)較慢。
方法區(qū): 方法區(qū)中可以 存儲(chǔ)字符串常量池、靜態(tài)數(shù)據(jù)、代碼和類的元數(shù)據(jù)。
我們知道,數(shù)組屬于引用類型,而數(shù)組的引用變量(數(shù)組名稱)只是一個(gè)地址引用。 這個(gè)引用變量可以指向任何有效的內(nèi)存空間,只有當(dāng)這個(gè)引用指向有效的空間時(shí),才可以通過(guò)引用去操作數(shù)組中真正的數(shù)據(jù)元素。所以數(shù)組的引用變量(數(shù)組名稱)是存儲(chǔ)在??臻g中,但真正的數(shù)組數(shù)據(jù)是存儲(chǔ)在堆空間中。
2. 代碼案例
為了讓大家更好地理解數(shù)組的內(nèi)存結(jié)構(gòu),接下來(lái)給大家設(shè)計(jì)一個(gè)代碼案例,然后給大家分析一下這個(gè)數(shù)組的內(nèi)存結(jié)構(gòu)。
/**
* @author
*/
public class Demo01 {
public static void main(String[] args) {
//使用靜態(tài)初始化的方式初始化一個(gè)數(shù)組a
//a存放在棧中,a的值是數(shù)組的地址,數(shù)組的真正數(shù)據(jù){5,7,20}存放在堆中
int[] a = {5,7,20};
System.out.println("a的長(zhǎng)度為:" + a.length);//3
//整型變量,存放在棧中
int num =8;
System.out.println("num:"+num);
//定義一個(gè)新的數(shù)組b
int[] b=new int[4];
System.out.println("b的長(zhǎng)度是:"+b.length);
//將a賦值給b,是b的指向改變了,但b原先對(duì)應(yīng)的數(shù)組依然存在
b=a;
System.out.println("b的長(zhǎng)度是:"+b.length);
}
}
3. 內(nèi)存分析
為了讓各位更好地理解基本類型的數(shù)據(jù)和數(shù)組的內(nèi)存結(jié)構(gòu),再給大家繪制下面一張圖。

根據(jù)上面的代碼和下面的內(nèi)存分析圖,我們可以得到如下結(jié)論:
- 變量a存放在棧中,a的值是數(shù)組的首地址,數(shù)組的真正數(shù)據(jù){5,7,20}存放在堆中;
- 整型變量num存放在棧中;
- 定義新的數(shù)組b,數(shù)組名稱b存放在棧中,b的數(shù)據(jù)在堆中;
- 將a賦值給b,此時(shí)b的指向改變了,但b原先對(duì)應(yīng)的數(shù)組依然存在,此時(shí)b指向原先a對(duì)應(yīng)的數(shù)組數(shù)據(jù)。
二. 數(shù)組擴(kuò)容與縮容
1. 擴(kuò)容簡(jiǎn)介
在前面給大家說(shuō)過(guò),數(shù)組一旦創(chuàng)建初始化后,其長(zhǎng)度就不能被改變。但是有的小伙伴就說(shuō)了,”不對(duì)啊,我看別人的文章說(shuō),可以往數(shù)組中增加很多新數(shù)據(jù)啊......“。那如果是這樣,假如我們一開(kāi)始定義一個(gè)長(zhǎng)度為5的數(shù)組,然后想把10個(gè)數(shù)據(jù)元素都插進(jìn)去,這能不能實(shí)現(xiàn)?
大家想一下,你能把10升水裝到5升的瓶子中嗎?肯定不行!如果你非要把10升水都裝到瓶子里,肯定需要換一個(gè)新的更大的瓶子!
所以今天跟大家說(shuō)的”數(shù)組擴(kuò)容“,其實(shí)并不是將這些多余的數(shù)據(jù)裝到原有的數(shù)組中,而是創(chuàng)建一個(gè)新的更大的數(shù)組,再把原有數(shù)組中的內(nèi)容都復(fù)制到新數(shù)組中來(lái)!
2. 擴(kuò)容與縮容流程(重點(diǎn))
在Java中,數(shù)組的”擴(kuò)容“和”縮容“,并不是真的改變?cè)袛?shù)組的大小,而是創(chuàng)建一個(gè)新的數(shù)組,然后再進(jìn)行操作,具體流程如下:
- 步驟1: 定義一個(gè)新數(shù)組,新數(shù)組的長(zhǎng)度要比原數(shù)組增加或者減??;
- 步驟2: 將原數(shù)組中的元素拷貝到新數(shù)組中;
- 步驟3:將原數(shù)組的名稱變量指向新數(shù)組。
3. 代碼實(shí)現(xiàn)
接下來(lái)就按照上面的流程,來(lái)帶大家實(shí)現(xiàn)一下數(shù)組的擴(kuò)容和縮容。
3.1 擴(kuò)容代碼
以下代碼是進(jìn)行數(shù)組擴(kuò)容的案例。
/**
* @author
*/
public class Demo05 {
public static void main(String[] args) {
// 數(shù)組擴(kuò)容
// 原數(shù)組
int[] oldArr = { 1, 3, 46, 22, 11 };
// 1.定義一個(gè)新數(shù)組,長(zhǎng)度比原數(shù)組的長(zhǎng)度多1,用于擴(kuò)容
int[] newArr = new int[oldArr.length + 1];
// 2.數(shù)組拷貝
for (int i = 0; i < oldArr.length; i++) {
//數(shù)組拷貝,將原來(lái)數(shù)組的元素拷貝到新數(shù)組中
newArr[i] = oldArr[i];
}
// 3.將原數(shù)組的名稱變量指向新數(shù)組
oldArr = newArr;
System.out.println("數(shù)組長(zhǎng)度="+oldArr.length);
//4.遍歷數(shù)組
for (int i = 0; i < oldArr.length; i++) {
//最后一個(gè)元素的值是默認(rèn)值0
System.out.println(oldArr[i]);
}
}
}
這里我們使用newArr[i] = oldArr[i];這樣的語(yǔ)句,將舊數(shù)組中的元素拷貝到新數(shù)組中
3.2 縮容代碼
以下代碼是進(jìn)行數(shù)組縮容的案例,供大家參考:
/**
* @author
*/
public class Demo06 {
public static void main(String[] args) {
// 數(shù)組縮容
//定義一個(gè)原數(shù)組
int[] oldArr = {1,3,46,22,11};
//1.定義一個(gè)新數(shù)組,新數(shù)組的長(zhǎng)度比原數(shù)組長(zhǎng)度少1個(gè)
int[] newArr = new int[oldArr.length-1];
//2.進(jìn)行數(shù)組拷貝,將舊數(shù)組中的元素拷貝到新數(shù)組中
for (int i = 0; i < newArr.length; i++) {
newArr[i] = oldArr[i];
}
//3.將原數(shù)組的名稱變量指向新數(shù)組
oldArr = newArr;
for (int i = 0; i < newArr.length; i++) {
System.out.println(oldArr[i]);
}
}
}
三. 數(shù)組拷貝
在給大家講解數(shù)組擴(kuò)容時(shí),涉及到了數(shù)組中數(shù)據(jù)元素的拷貝復(fù)制。那么除了上面的拷貝方式之外,數(shù)組還有哪些拷貝方式呢?
1. 拷貝方式
在Java中,數(shù)組的拷貝主要有三種實(shí)現(xiàn)方式:
通過(guò)循環(huán)語(yǔ)句,將原數(shù)組中的各個(gè)元素拷貝到新數(shù)組中(即數(shù)組擴(kuò)容案例中使用的方法);
System類提供的數(shù)組拷貝方法;
Arrays類提供的數(shù)組拷貝方法。
接下來(lái)就設(shè)計(jì)幾個(gè)案例,來(lái)給大家展示這幾種方式都是怎么進(jìn)行數(shù)組拷貝的。因?yàn)榈谝环N數(shù)組拷貝方式,我們已經(jīng)在數(shù)組擴(kuò)容的案例中給大家演示了,這里就不再重復(fù)展示相關(guān)代碼了。
2. System.arraycopy方法
2.1 簡(jiǎn)介
System.arraycopy()是Java提供的一個(gè)本地靜態(tài)方法,用于將數(shù)據(jù)元素從源數(shù)組復(fù)制到目標(biāo)數(shù)組。
public static native void arraycopy(Object src,int srcPos,Object dest,int destPos,int length);
arraycopy()方法有5個(gè)核心參數(shù),其含義如下:
- src: 源數(shù)組,即被復(fù)制的舊數(shù)組;
- srcPos: 源數(shù)組中開(kāi)始復(fù)制的索引位置;
- dest: 目標(biāo)數(shù)組,即要復(fù)制到的新數(shù)組;
- destPos: 要復(fù)制到目標(biāo)數(shù)組中的索引位置;
- length: 要復(fù)制的元素個(gè)數(shù)。
另外我們還要注意,arraycopy()方法在有些情況下有可能會(huì)發(fā)生如下異常:
- NullPointerException: if source or destination array is null.NullPointerException :如果源或目標(biāo)數(shù)組為null時(shí),就會(huì)產(chǎn)生NullPointerException異常。
- ArrayStoreException: if the source and destination array type doesn’t match or they are not array.ArrayStoreException :如果源和目標(biāo)數(shù)組類型不匹配或不是數(shù)組,會(huì)產(chǎn)生該異常;
- ArrayIndexOutOfBoundsException: if the data overflow occurs because of index values or they are negative.ArrayIndexOutOfBoundsException :如果由于索引值導(dǎo)致數(shù)據(jù)溢出,或它們?yōu)樨?fù)數(shù)時(shí)會(huì)產(chǎn)生該異常。
2.2 案例
我們先來(lái)看看下面這個(gè)數(shù)組拷貝的案例:
/**
* @author
*/
public class Demo07 {
public static void main(String[] args) {
// 數(shù)組拷貝
//1.源數(shù)組
int[] srcArr = {1,3,46,22,11};
//2.目標(biāo)數(shù)組
int[] destArr = new int[srcArr.length + 5];
/**
* src:原數(shù)組
* srcPos:原數(shù)組的起始拷貝位置
* dest:目標(biāo)數(shù)組
* destPos:目標(biāo)數(shù)組的起始拷貝位置
* length:拷貝的長(zhǎng)度
*/
//3.調(diào)用arraycopy方法進(jìn)行復(fù)制
System.arraycopy(srcArr, 1, destArr, 3, 4);
//對(duì)新數(shù)組進(jìn)行遍歷
for (int i = 0; i < destArr.length; i++) {
System.out.print(destArr[i]+"\t");
}
}
}
3. Arrays.copyOf方法
3.1 簡(jiǎn)介
Arrays.copyOf()可以復(fù)制數(shù)組中指定范圍的元素。該方法會(huì)返回一個(gè)新的數(shù)組對(duì)象,且改變新數(shù)組中的元素值,不會(huì)影響原來(lái)的數(shù)組。我們還可以利用Arrays.toString方法將賦值后的數(shù)組輸出。
該方法支持的參數(shù)可以是long、float、double、int、boolean、byte、Object等類型的數(shù)組。
public static int[] copyOf(int[] original, int newLength);
copyOf()方法有2個(gè)核心參數(shù),其含義如下:
- original: 源數(shù)組,即被復(fù)制的舊數(shù)組;
- newLength: 表示新數(shù)組的長(zhǎng)度。如果新數(shù)組的長(zhǎng)度超過(guò)源數(shù)組的長(zhǎng)度,會(huì)采用數(shù)組元素類型的默認(rèn)值。
3.2 案例
以下是Arrays.copyOf()方法的實(shí)現(xiàn)案例:
/**
* @author
*/
public class Demo08 {
public static void main(String[] args) {
// 數(shù)組拷貝
//1.源數(shù)組
int[] srcArr = {1,3,46,22,11};
/**
* original:原數(shù)組
* newLength:新數(shù)組的長(zhǎng)度
* 返回值:返回新數(shù)組
*/
//2.調(diào)用copyOf方法進(jìn)行數(shù)組拷貝
int[] destArr = Arrays.copyOf(srcArr, srcArr.length+1);
//3.遍歷新數(shù)組
for (int i = 0; i < srcArr.length; i++) {
System.out.print(destArr[i]+"\t");
}
}
}
四. 結(jié)語(yǔ)
至此,就把數(shù)組的擴(kuò)容、縮容及拷貝等內(nèi)容給大家介紹完畢了,現(xiàn)在你明白數(shù)組的擴(kuò)容原理了嗎?
以上就是詳解Java數(shù)組擴(kuò)容縮容與拷貝的實(shí)現(xiàn)和原理的詳細(xì)內(nèi)容,更多關(guān)于Java數(shù)組擴(kuò)容縮容與拷貝的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Required?request?body?is?missing的問(wèn)題及解決
這篇文章主要介紹了Required?request?body?is?missing的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Java微信公眾平臺(tái)之群發(fā)接口(高級(jí)群發(fā))
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)之群發(fā)接口,高級(jí)群發(fā)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
JAVA 數(shù)據(jù)結(jié)構(gòu)之Queue處理實(shí)例代碼
這篇文章主要介紹了JAVA 數(shù)據(jù)結(jié)構(gòu)之Queue處理實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02
Java優(yōu)先隊(duì)列(PriorityQueue)重寫(xiě)compare操作
這篇文章主要介紹了Java優(yōu)先隊(duì)列(PriorityQueue)重寫(xiě)compare操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
SpringBoot實(shí)現(xiàn)過(guò)濾器和攔截器的方法
大家應(yīng)該都曉得實(shí)現(xiàn)過(guò)濾器需要實(shí)現(xiàn)?javax.servlet.Filter?接口,而攔截器會(huì)在處理指定請(qǐng)求之前和之后進(jìn)行相關(guān)操作,配置攔截器需要兩步,本文通過(guò)實(shí)例代碼給大家介紹SpringBoot?過(guò)濾器和攔截器的相關(guān)知識(shí),感興趣的朋友一起看看吧2022-11-11
Spring Cloud微服務(wù)使用webSocket的方法
WebSocket在現(xiàn)代瀏覽器中的應(yīng)用已經(jīng)算是比較普遍了,在某些業(yè)務(wù)場(chǎng)景下,要求必須能夠在服務(wù)器端推送消息至客戶端,本文給大家介紹Spring Cloud微服務(wù)使用webSocket的方法,感興趣的朋友一起看看吧2021-06-06
Java定時(shí)清理過(guò)期文件的實(shí)例代碼
這篇文章主要介紹了Java定時(shí)清理過(guò)期文件的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2018-12-12

